@brantrusnak/openclaw-omadeus 1.0.3 → 1.0.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 +13 -62
- package/dist/src/api/auth.api.js +29 -26
- package/dist/src/channel.js +8 -6
- package/dist/src/config.js +9 -4
- package/dist/src/defaults.js +30 -4
- package/dist/src/onboarding.js +52 -49
- package/dist/src/setup-core.js +4 -4
- package/openclaw.plugin.json +22 -11
- package/package.json +1 -1
- package/src/api/auth.api.ts +27 -7
- package/src/channel.ts +9 -5
- package/src/config.ts +14 -4
- package/src/defaults.ts +44 -2
- package/src/onboarding.ts +97 -78
- package/src/setup-core.ts +4 -4
- package/src/types.ts +6 -2
package/README.md
CHANGED
|
@@ -1,61 +1,44 @@
|
|
|
1
1
|
# OpenClaw Omadeus Plugin
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
[](https://badge.socket.dev/npm/package/@brantrusnak/openclaw-omadeus)
|
|
4
|
+
[](https://github.com/brantrusnak/openclaw-omadeus-plugin/actions/workflows/npm-publish.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/@brantrusnak/openclaw-omadeus)
|
|
6
|
+
[](https://www.npmjs.com/package/@brantrusnak/openclaw-omadeus)
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
for Omadeus messages and reply through the selected Omadeus channel.
|
|
8
|
+
[Omadeus](https://omadeus.com) plugin for [OpenClaw](https://www.npmjs.com/package/openclaw).
|
|
7
9
|
|
|
8
10
|
## Requirements
|
|
9
11
|
|
|
10
12
|
- Node.js 22 or newer
|
|
11
13
|
- OpenClaw 2026.4.10 or newer
|
|
12
|
-
- An Omadeus account
|
|
13
|
-
OpenClaw to use
|
|
14
|
+
- An Omadeus account
|
|
14
15
|
|
|
15
16
|
## Install
|
|
16
17
|
|
|
17
|
-
Install OpenClaw first:
|
|
18
|
-
|
|
19
18
|
```bash
|
|
20
19
|
npm install -g openclaw
|
|
20
|
+
openclaw plugins install @brantrusnak/openclaw-omadeus
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
openclaw onboard
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Then install this plugin with the OpenClaw plugin command:
|
|
23
|
+
Verify the plugin was installed:
|
|
30
24
|
|
|
31
25
|
```bash
|
|
32
|
-
openclaw plugins
|
|
26
|
+
openclaw plugins list
|
|
33
27
|
```
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
Then run setup:
|
|
36
30
|
|
|
37
31
|
```bash
|
|
38
|
-
openclaw
|
|
39
|
-
openclaw plugins inspect omadeus
|
|
32
|
+
openclaw onboard
|
|
40
33
|
```
|
|
41
34
|
|
|
42
35
|
## Configure
|
|
43
36
|
|
|
44
|
-
After installing the plugin, run OpenClaw configuration and choose Omadeus when
|
|
45
|
-
prompted:
|
|
46
|
-
|
|
47
37
|
```bash
|
|
48
38
|
openclaw configure
|
|
49
39
|
```
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- Omadeus email and password
|
|
54
|
-
- Organization ID
|
|
55
|
-
- The Omadeus member/account to listen as
|
|
56
|
-
- The Omadeus channel to use for messages
|
|
57
|
-
|
|
58
|
-
The plugin also supports these environment variables:
|
|
41
|
+
You can also set credentials via environment variables:
|
|
59
42
|
|
|
60
43
|
```bash
|
|
61
44
|
export OMADEUS_EMAIL="you@example.com"
|
|
@@ -63,48 +46,16 @@ export OMADEUS_PASSWORD="your-password"
|
|
|
63
46
|
export OMADEUS_ORGANIZATION_ID="123"
|
|
64
47
|
```
|
|
65
48
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- CAS: `https://dev1-cas.rouztech.com`
|
|
69
|
-
- Maestro: `https://dev3-maestro.rouztech.com`
|
|
70
|
-
|
|
71
|
-
If your Omadeus deployment uses different endpoints, configure them through the
|
|
72
|
-
OpenClaw setup flow or in your OpenClaw config under `channels.omadeus`.
|
|
73
|
-
|
|
74
|
-
## Start OpenClaw
|
|
75
|
-
|
|
76
|
-
Once OpenClaw and the plugin are configured, start or restart the gateway:
|
|
49
|
+
## Start
|
|
77
50
|
|
|
78
51
|
```bash
|
|
79
52
|
openclaw gateway
|
|
80
53
|
```
|
|
81
54
|
|
|
82
|
-
Check channel health with:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
openclaw channels status --deep
|
|
86
|
-
openclaw plugins doctor
|
|
87
|
-
```
|
|
88
|
-
|
|
89
55
|
## Local Development
|
|
90
56
|
|
|
91
|
-
Build the runtime files before linking a local checkout into OpenClaw:
|
|
92
|
-
|
|
93
57
|
```bash
|
|
94
58
|
npm install
|
|
95
59
|
npm run build
|
|
96
60
|
openclaw plugins install . --link
|
|
97
61
|
```
|
|
98
|
-
|
|
99
|
-
Before publishing, inspect the npm package contents:
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
npm run prepack
|
|
103
|
-
npm pack --dry-run
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Publish to npm:
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
npm publish
|
|
110
|
-
```
|
package/dist/src/api/auth.api.js
CHANGED
|
@@ -2,20 +2,29 @@ import { getCasSession, setCasSession } from "../store.js";
|
|
|
2
2
|
//#region src/api/auth.api.ts
|
|
3
3
|
const CAS_APPLICATION_ID = 1;
|
|
4
4
|
const CAS_SCOPES = "title,email,avatar,firstName,lastName,birth,phone,countryCode";
|
|
5
|
+
function formatFetchError(label, url, method, err) {
|
|
6
|
+
const base = err instanceof Error ? err.message : String(err);
|
|
7
|
+
const cause = err instanceof Error && err.cause instanceof Error ? err.cause.message : void 0;
|
|
8
|
+
const detail = cause && cause !== base ? `${base} (${cause})` : base;
|
|
9
|
+
return /* @__PURE__ */ new Error(`${label} (${method} ${url}) failed: ${detail}`);
|
|
10
|
+
}
|
|
11
|
+
async function omadeusFetch(label, url, init) {
|
|
12
|
+
const method = init.method ?? "GET";
|
|
13
|
+
try {
|
|
14
|
+
return await fetch(url, init);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
throw formatFetchError(label, url, method, err);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
5
19
|
async function createCasToken(params) {
|
|
6
20
|
const { casUrl, email, password } = params;
|
|
7
|
-
const
|
|
8
|
-
const jsonBody = JSON.stringify({
|
|
9
|
-
email,
|
|
10
|
-
password
|
|
11
|
-
});
|
|
12
|
-
const res = await fetch(url, {
|
|
21
|
+
const res = await omadeusFetch("CAS token request", `${casUrl}/apiv1/tokens`, {
|
|
13
22
|
method: "CREATE",
|
|
14
|
-
headers: {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
headers: { "Content-Type": "application/json;charset=UTF-8" },
|
|
24
|
+
body: JSON.stringify({
|
|
25
|
+
email,
|
|
26
|
+
password
|
|
27
|
+
})
|
|
19
28
|
});
|
|
20
29
|
if (!res.ok) {
|
|
21
30
|
const text = await res.text().catch(() => "");
|
|
@@ -42,16 +51,13 @@ async function createAuthorizationCode(params) {
|
|
|
42
51
|
redirectUri: redirectUri ?? ""
|
|
43
52
|
});
|
|
44
53
|
if (redirectUri) qs.set("redirectUri", redirectUri);
|
|
45
|
-
const
|
|
46
|
-
const body = "";
|
|
47
|
-
const headers = {
|
|
48
|
-
Authorization: `Bearer ${token}`,
|
|
49
|
-
...casSession?.refreshCookie ? { Cookie: casSession.refreshCookie } : {}
|
|
50
|
-
};
|
|
51
|
-
const res = await fetch(url, {
|
|
54
|
+
const res = await omadeusFetch("CAS authorization code request", `${casUrl}/apiv1/authorizationcodes?${qs}`, {
|
|
52
55
|
method: "CREATE",
|
|
53
|
-
body,
|
|
54
|
-
headers
|
|
56
|
+
body: "",
|
|
57
|
+
headers: {
|
|
58
|
+
Authorization: `Bearer ${token}`,
|
|
59
|
+
...casSession?.refreshCookie ? { Cookie: casSession.refreshCookie } : {}
|
|
60
|
+
}
|
|
55
61
|
});
|
|
56
62
|
if (!res.ok) {
|
|
57
63
|
const text = await res.text().catch(() => "");
|
|
@@ -64,8 +70,7 @@ async function createAuthorizationCode(params) {
|
|
|
64
70
|
}
|
|
65
71
|
async function obtainSessionToken(params) {
|
|
66
72
|
const { maestroUrl, authorizationCode, organizationId } = params;
|
|
67
|
-
const
|
|
68
|
-
const res = await fetch(url, {
|
|
73
|
+
const res = await omadeusFetch("Omadeus session token request", `${maestroUrl}/dolphin/apiv1/oauth2/tokens`, {
|
|
69
74
|
method: "OBTAIN",
|
|
70
75
|
headers: { "Content-Type": "application/json;charset=UTF-8" },
|
|
71
76
|
body: JSON.stringify({
|
|
@@ -83,8 +88,7 @@ async function obtainSessionToken(params) {
|
|
|
83
88
|
}
|
|
84
89
|
async function listOrganizations(params) {
|
|
85
90
|
const { maestroUrl, email } = params;
|
|
86
|
-
const
|
|
87
|
-
const res = await fetch(url, {
|
|
91
|
+
const res = await omadeusFetch("Omadeus list organizations", `${maestroUrl}/dolphin/apiv1/organizations`, {
|
|
88
92
|
method: "LIST",
|
|
89
93
|
headers: { "Content-Type": "application/json;charset=UTF-8" },
|
|
90
94
|
body: JSON.stringify({ email })
|
|
@@ -97,8 +101,7 @@ async function listOrganizations(params) {
|
|
|
97
101
|
}
|
|
98
102
|
async function listOrganizationMembers(params) {
|
|
99
103
|
const { maestroUrl, sessionToken, organizationId } = params;
|
|
100
|
-
const
|
|
101
|
-
const res = await fetch(url, {
|
|
104
|
+
const res = await omadeusFetch("Omadeus list organization members", `${maestroUrl}/dolphin/apiv1/organizations/${organizationId}/members`, {
|
|
102
105
|
method: "LIST",
|
|
103
106
|
headers: {
|
|
104
107
|
Authorization: `Bearer ${sessionToken}`,
|
package/dist/src/channel.js
CHANGED
|
@@ -27,10 +27,11 @@ const gatewayState = {
|
|
|
27
27
|
};
|
|
28
28
|
const isUnconfigured = (account) => account.credentialSource === "none";
|
|
29
29
|
let lastPersistedToken = null;
|
|
30
|
-
async function persistSessionToken(token) {
|
|
30
|
+
async function persistSessionToken(token, environment) {
|
|
31
31
|
if (lastPersistedToken === token) return;
|
|
32
32
|
const runtime = getOmadeusRuntime();
|
|
33
|
-
|
|
33
|
+
const section = getOmadeusChannelConfig(runtime.config.current()) ?? {};
|
|
34
|
+
if (section.sessionToken === token && section.sessionTokenEnvironment === environment) {
|
|
34
35
|
lastPersistedToken = token;
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
@@ -41,7 +42,8 @@ async function persistSessionToken(token) {
|
|
|
41
42
|
...draft.channels ?? {},
|
|
42
43
|
omadeus: {
|
|
43
44
|
...getOmadeusChannelConfig(draft) ?? {},
|
|
44
|
-
sessionToken: token
|
|
45
|
+
sessionToken: token,
|
|
46
|
+
sessionTokenEnvironment: environment
|
|
45
47
|
}
|
|
46
48
|
};
|
|
47
49
|
}
|
|
@@ -82,12 +84,12 @@ const omadeusConfigAdapter = createTopLevelChannelConfigAdapter({
|
|
|
82
84
|
defaultAccountId: resolveDefaultOmadeusAccountId,
|
|
83
85
|
deleteMode: "clear-fields",
|
|
84
86
|
clearBaseFields: [
|
|
85
|
-
"
|
|
86
|
-
"maestroUrl",
|
|
87
|
+
"environment",
|
|
87
88
|
"email",
|
|
88
89
|
"password",
|
|
89
90
|
"organizationId",
|
|
90
91
|
"sessionToken",
|
|
92
|
+
"sessionTokenEnvironment",
|
|
91
93
|
"inbound"
|
|
92
94
|
],
|
|
93
95
|
resolveAllowFrom: () => [],
|
|
@@ -473,7 +475,7 @@ const omadeusPlugin = {
|
|
|
473
475
|
initialToken: account.sessionToken,
|
|
474
476
|
onRefresh: (token) => {
|
|
475
477
|
log.info("[omadeus] token refreshed");
|
|
476
|
-
persistSessionToken(token).catch((err) => log.warn(`[omadeus] failed to persist session token: ${String(err)}`));
|
|
478
|
+
persistSessionToken(token, account.environment).catch((err) => log.warn(`[omadeus] failed to persist session token: ${String(err)}`));
|
|
477
479
|
},
|
|
478
480
|
onError: (err) => {
|
|
479
481
|
log.error(`[omadeus] token refresh failed: ${err.message}`);
|
package/dist/src/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_ACCOUNT_ID } from "../runtime-api.js";
|
|
2
|
-
import "./defaults.js";
|
|
2
|
+
import { getOmadeusEnvironmentUrls, resolveOmadeusEnvironment } from "./defaults.js";
|
|
3
3
|
//#region src/config.ts
|
|
4
4
|
function getOmadeusChannelConfig(cfg) {
|
|
5
5
|
return cfg.channels?.["omadeus"];
|
|
@@ -14,11 +14,15 @@ function resolveDefaultOmadeusAccountId(_cfg) {
|
|
|
14
14
|
function resolveOmadeusAccount(params) {
|
|
15
15
|
const { cfg } = params;
|
|
16
16
|
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
17
|
+
const environment = resolveOmadeusEnvironment(section.environment);
|
|
18
|
+
const { casUrl, maestroUrl } = getOmadeusEnvironmentUrls(environment);
|
|
17
19
|
const envCredentials = resolveOmadeusEnvCredentials();
|
|
18
20
|
const email = section.email?.trim() || envCredentials?.email || "";
|
|
19
21
|
const password = section.password?.trim() || envCredentials?.password || "";
|
|
20
22
|
const orgId = section.organizationId ?? envCredentials?.organizationId;
|
|
21
|
-
const
|
|
23
|
+
const rawSessionToken = section.sessionToken?.trim() ?? "";
|
|
24
|
+
const sessionTokenEnvironment = resolveOmadeusEnvironment(section.sessionTokenEnvironment);
|
|
25
|
+
const sessionToken = Boolean(rawSessionToken) && sessionTokenEnvironment === environment ? rawSessionToken : "";
|
|
22
26
|
const hasCredentials = Boolean(email && password && orgId);
|
|
23
27
|
const hasSessionToken = Boolean(sessionToken);
|
|
24
28
|
const credentialSource = Boolean(section.email?.trim() && section.password?.trim() && section.organizationId) ? "config" : hasCredentials ? "env" : hasSessionToken ? "session" : "none";
|
|
@@ -27,8 +31,9 @@ function resolveOmadeusAccount(params) {
|
|
|
27
31
|
name: "Omadeus",
|
|
28
32
|
enabled: section.enabled !== false,
|
|
29
33
|
config: section,
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
environment,
|
|
35
|
+
casUrl,
|
|
36
|
+
maestroUrl,
|
|
32
37
|
email,
|
|
33
38
|
password,
|
|
34
39
|
organizationId: orgId ?? 0,
|
package/dist/src/defaults.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const OMADEUS_ENVIRONMENTS = {
|
|
2
|
+
production: {
|
|
3
|
+
label: "Production",
|
|
4
|
+
casUrl: "https://xas.xeba.tech",
|
|
5
|
+
maestroUrl: "https://maestro.xeba.tech"
|
|
6
|
+
},
|
|
7
|
+
staging: {
|
|
8
|
+
label: "Staging",
|
|
9
|
+
casUrl: "https://staging-xas.xeba.tech",
|
|
10
|
+
maestroUrl: "https://staging.xeba.tech"
|
|
11
|
+
},
|
|
12
|
+
dev: {
|
|
13
|
+
label: "Dev",
|
|
14
|
+
casUrl: "https://dev1-cas.rouztech.com",
|
|
15
|
+
maestroUrl: "https://dev1-maestro.rouztech.com"
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const OMADEUS_ENVIRONMENT_SET = new Set(Object.keys(OMADEUS_ENVIRONMENTS));
|
|
19
|
+
function resolveOmadeusEnvironment(value) {
|
|
20
|
+
if (typeof value === "string" && OMADEUS_ENVIRONMENT_SET.has(value)) return value;
|
|
21
|
+
return "dev";
|
|
22
|
+
}
|
|
23
|
+
function getOmadeusEnvironmentUrls(env) {
|
|
24
|
+
const config = OMADEUS_ENVIRONMENTS[env];
|
|
25
|
+
return {
|
|
26
|
+
casUrl: config.casUrl,
|
|
27
|
+
maestroUrl: config.maestroUrl
|
|
28
|
+
};
|
|
29
|
+
}
|
|
4
30
|
//#endregion
|
|
5
|
-
export {
|
|
31
|
+
export { OMADEUS_ENVIRONMENTS, getOmadeusEnvironmentUrls, resolveOmadeusEnvironment };
|
package/dist/src/onboarding.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OMADEUS_ENVIRONMENTS, getOmadeusEnvironmentUrls, resolveOmadeusEnvironment } from "./defaults.js";
|
|
2
2
|
import { getOmadeusChannelConfig, resolveOmadeusAccount } from "./config.js";
|
|
3
3
|
import { listOrganizationMembers, listOrganizations } from "./api/auth.api.js";
|
|
4
4
|
import { formatMemberLabel } from "./member-resolve.js";
|
|
@@ -8,18 +8,37 @@ import { authenticate } from "./auth.js";
|
|
|
8
8
|
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
|
9
9
|
//#region src/onboarding.ts
|
|
10
10
|
const channel = "omadeus";
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
function formatAuthError(err) {
|
|
12
|
+
if (!(err instanceof Error)) return String(err);
|
|
13
|
+
const parts = [err.message];
|
|
14
|
+
const { cause } = err;
|
|
15
|
+
if (cause instanceof Error) {
|
|
16
|
+
parts.push(cause.message);
|
|
17
|
+
const code = cause.code;
|
|
18
|
+
if (typeof code === "string" && code) parts.push(`(${code})`);
|
|
19
|
+
} else if (typeof cause === "string" && cause.trim()) parts.push(cause);
|
|
20
|
+
return parts.join(" — ");
|
|
21
|
+
}
|
|
22
|
+
async function noteOmadeusAuthHelp(prompter, environment) {
|
|
23
|
+
const envLabel = OMADEUS_ENVIRONMENTS[environment].label;
|
|
13
24
|
await prompter.note([
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
" - Organization ID (we can look it up for you)",
|
|
18
|
-
`CAS URL: ${OMADEUS_CAS_URL}`,
|
|
19
|
-
`Maestro URL: ${OMADEUS_MAESTRO_URL}`,
|
|
20
|
-
"Env vars supported: OMADEUS_EMAIL, OMADEUS_PASSWORD, OMADEUS_ORGANIZATION_ID."
|
|
25
|
+
`Connect OpenClaw to Omadeus (${envLabel}).`,
|
|
26
|
+
"",
|
|
27
|
+
"We'll ask for your email and password, then show the organizations on your account so you can pick one."
|
|
21
28
|
].join("\n"), "Omadeus setup");
|
|
22
29
|
}
|
|
30
|
+
async function promptEnvironment(prompter, existing) {
|
|
31
|
+
const initial = existing ?? "dev";
|
|
32
|
+
return resolveOmadeusEnvironment(await prompter.select({
|
|
33
|
+
message: "Select Omadeus environment",
|
|
34
|
+
options: Object.keys(OMADEUS_ENVIRONMENTS).map((env) => ({
|
|
35
|
+
value: env,
|
|
36
|
+
label: OMADEUS_ENVIRONMENTS[env].label,
|
|
37
|
+
hint: getOmadeusEnvironmentUrls(env).maestroUrl
|
|
38
|
+
})),
|
|
39
|
+
initialValue: initial
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
23
42
|
async function promptOrganizationId(params) {
|
|
24
43
|
const { prompter, maestroUrl, email, existing } = params;
|
|
25
44
|
try {
|
|
@@ -66,7 +85,14 @@ async function promptChannelSelection(params) {
|
|
|
66
85
|
skip: 0,
|
|
67
86
|
take: 100
|
|
68
87
|
});
|
|
69
|
-
if (channels.length === 0)
|
|
88
|
+
if (channels.length === 0) {
|
|
89
|
+
await prompter.note("No channels found for this account. Channel listening will stay disabled.", "Omadeus channels");
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
if (!await prompter.confirm({
|
|
93
|
+
message: "Listen for messages in Omadeus channels?",
|
|
94
|
+
initialValue: (existingChannelViewIds?.length ?? 0) > 0
|
|
95
|
+
})) return [];
|
|
70
96
|
const selected = await promptMultiSelect({
|
|
71
97
|
prompter,
|
|
72
98
|
message: "Which channels should OpenClaw listen to?",
|
|
@@ -75,11 +101,9 @@ async function promptChannelSelection(params) {
|
|
|
75
101
|
label: item.title || `Channel ${item.id}`,
|
|
76
102
|
hint: [item.privateRoomId ? `private:${item.privateRoomId}` : void 0, item.publicRoomId ? `public:${item.publicRoomId}` : void 0].filter(Boolean).join(" | ")
|
|
77
103
|
})),
|
|
78
|
-
initialValues: existingChannelViewIds && existingChannelViewIds.length > 0 ? existingChannelViewIds.map(String) :
|
|
104
|
+
initialValues: existingChannelViewIds && existingChannelViewIds.length > 0 ? existingChannelViewIds.map(String) : void 0
|
|
79
105
|
});
|
|
80
|
-
|
|
81
|
-
if (chosen.length === 0) throw new Error("At least one channel must be selected.");
|
|
82
|
-
return chosen;
|
|
106
|
+
return channels.filter((item) => selected.includes(String(item.id)));
|
|
83
107
|
}
|
|
84
108
|
function memberHint(member) {
|
|
85
109
|
const parts = [
|
|
@@ -90,32 +114,11 @@ function memberHint(member) {
|
|
|
90
114
|
return parts.length > 0 ? parts.join(" | ") : void 0;
|
|
91
115
|
}
|
|
92
116
|
async function promptMultiSelect(params) {
|
|
93
|
-
|
|
94
|
-
const runMulti = multi.multiSelect ?? multi.multiselect;
|
|
95
|
-
if (runMulti) return runMulti({
|
|
117
|
+
return params.prompter.multiselect({
|
|
96
118
|
message: params.message,
|
|
97
119
|
options: params.options,
|
|
98
|
-
initialValues: params.initialValues
|
|
99
|
-
initialValue: params.initialValues
|
|
120
|
+
initialValues: params.initialValues
|
|
100
121
|
});
|
|
101
|
-
const selected = new Set(params.initialValues ?? []);
|
|
102
|
-
while (true) {
|
|
103
|
-
const next = await params.prompter.select({
|
|
104
|
-
message: `${params.message} (${selected.size} selected)`,
|
|
105
|
-
options: [{
|
|
106
|
-
value: DONE,
|
|
107
|
-
label: selected.size > 0 ? "Done" : "Done (select none)"
|
|
108
|
-
}, ...params.options.map((option) => ({
|
|
109
|
-
...option,
|
|
110
|
-
label: selected.has(option.value) ? `[selected] ${option.label}` : option.label
|
|
111
|
-
}))],
|
|
112
|
-
initialValue: DONE
|
|
113
|
-
});
|
|
114
|
-
const value = String(next);
|
|
115
|
-
if (value === DONE) return [...selected];
|
|
116
|
-
if (selected.has(value)) selected.delete(value);
|
|
117
|
-
else selected.add(value);
|
|
118
|
-
}
|
|
119
122
|
}
|
|
120
123
|
async function loadSelectableMembers(params) {
|
|
121
124
|
const excluded = new Set(params.excludeReferenceIds ?? []);
|
|
@@ -214,11 +217,11 @@ const omadeusSetupWizard = {
|
|
|
214
217
|
const account = resolveOmadeusAccount({ cfg });
|
|
215
218
|
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
216
219
|
let next = cfg;
|
|
217
|
-
|
|
220
|
+
const environment = await promptEnvironment(prompter, section.environment ? resolveOmadeusEnvironment(section.environment) : void 0);
|
|
221
|
+
const { casUrl, maestroUrl } = getOmadeusEnvironmentUrls(environment);
|
|
222
|
+
if (account.credentialSource === "none") await noteOmadeusAuthHelp(prompter, environment);
|
|
218
223
|
const envEmail = process.env.OMADEUS_EMAIL?.trim();
|
|
219
224
|
const envPassword = process.env.OMADEUS_PASSWORD?.trim();
|
|
220
|
-
const casUrl = OMADEUS_CAS_URL;
|
|
221
|
-
const maestroUrl = OMADEUS_MAESTRO_URL;
|
|
222
225
|
let { email, password } = await promptCredentials(prompter, {
|
|
223
226
|
email: section.email ?? envEmail,
|
|
224
227
|
password: section.password ?? envPassword
|
|
@@ -244,8 +247,7 @@ const omadeusSetupWizard = {
|
|
|
244
247
|
await prompter.note(`Authenticated as ${payload.email}`, "Omadeus authentication");
|
|
245
248
|
break;
|
|
246
249
|
} catch (err) {
|
|
247
|
-
|
|
248
|
-
await prompter.note(`Authentication failed: ${msg}`, "Omadeus authentication");
|
|
250
|
+
await prompter.note(`Authentication failed: ${formatAuthError(err)}`, "Omadeus authentication");
|
|
249
251
|
if (!await prompter.confirm({
|
|
250
252
|
message: "Re-enter email/password and try again?",
|
|
251
253
|
initialValue: true
|
|
@@ -280,12 +282,12 @@ const omadeusSetupWizard = {
|
|
|
280
282
|
memberReferenceId: selfReferenceId,
|
|
281
283
|
existingChannelViewIds: existingInbound?.channels?.allowedChannelViewIds
|
|
282
284
|
});
|
|
283
|
-
const channelSenderIds = await promptSenderAllowlist({
|
|
285
|
+
const channelSenderIds = selectedChannels.length > 0 ? await promptSenderAllowlist({
|
|
284
286
|
prompter,
|
|
285
287
|
message: "Which users can trigger OpenClaw from allowed channels?",
|
|
286
288
|
members,
|
|
287
289
|
existingReferenceIds: existingInbound?.channels?.allowedSenderReferenceIds
|
|
288
|
-
});
|
|
290
|
+
}) : void 0;
|
|
289
291
|
const entityKinds = await promptEntityKindSelection({
|
|
290
292
|
prompter,
|
|
291
293
|
existingKinds: existingInbound?.entities?.allowedKinds
|
|
@@ -301,10 +303,11 @@ const omadeusSetupWizard = {
|
|
|
301
303
|
const channelTitles = selectedChannels.map((selectedChannel) => selectedChannel.title || `Channel ${selectedChannel.id}`).join(", ");
|
|
302
304
|
const senderSummary = (ids) => ids && ids.length > 0 ? ids.join(", ") : "all users";
|
|
303
305
|
const entityKindSummary = entityKinds.length > 0 ? entityKinds.join(", ") : "none (entity rooms disabled)";
|
|
306
|
+
const channelSummary = selectedChannels.length > 0 ? `- Channels "${channelTitles}": rooms ${channelRoomIds.join(", ") || "(no room ids)"} from ${senderSummary(channelSenderIds)}; @mention not required in those rooms.` : "- Channels: disabled (none selected).";
|
|
304
307
|
await prompter.note([
|
|
305
308
|
`Inbound policy (Jaguar chat):`,
|
|
306
309
|
`- Direct messages: enabled for ${senderSummary(directSenderIds)} (no @mention required).`,
|
|
307
|
-
|
|
310
|
+
channelSummary,
|
|
308
311
|
`- Entity rooms (${entityKindSummary}): ${senderSummary(entitySenderIds)}; @mention required.`
|
|
309
312
|
].join("\n"), "Omadeus inbound policy");
|
|
310
313
|
next = {
|
|
@@ -313,12 +316,12 @@ const omadeusSetupWizard = {
|
|
|
313
316
|
...next.channels,
|
|
314
317
|
omadeus: {
|
|
315
318
|
enabled: true,
|
|
316
|
-
|
|
317
|
-
maestroUrl,
|
|
319
|
+
environment,
|
|
318
320
|
email,
|
|
319
321
|
password,
|
|
320
322
|
organizationId,
|
|
321
323
|
sessionToken,
|
|
324
|
+
sessionTokenEnvironment: environment,
|
|
322
325
|
inbound: {
|
|
323
326
|
version: 1,
|
|
324
327
|
direct: {
|
|
@@ -327,7 +330,7 @@ const omadeusSetupWizard = {
|
|
|
327
330
|
requireMention: "never"
|
|
328
331
|
},
|
|
329
332
|
channels: {
|
|
330
|
-
enabled:
|
|
333
|
+
enabled: selectedChannels.length > 0,
|
|
331
334
|
allowedRoomIds: channelRoomIds,
|
|
332
335
|
allowedChannelViewIds: channelViewIds,
|
|
333
336
|
...channelSenderIds ? { allowedSenderReferenceIds: channelSenderIds } : {},
|
package/dist/src/setup-core.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveOmadeusEnvironment } from "./defaults.js";
|
|
1
2
|
//#region src/setup-core.ts
|
|
2
3
|
function readSetupStringField(input, key) {
|
|
3
4
|
const value = input[key];
|
|
@@ -18,8 +19,8 @@ const omadeusSetupAdapter = {
|
|
|
18
19
|
},
|
|
19
20
|
applyAccountConfig: ({ cfg, input }) => {
|
|
20
21
|
const rawInput = input;
|
|
21
|
-
const
|
|
22
|
-
const
|
|
22
|
+
const environmentRaw = readSetupStringField(rawInput, "environment");
|
|
23
|
+
const environment = environmentRaw ? resolveOmadeusEnvironment(environmentRaw) : void 0;
|
|
23
24
|
const email = readSetupStringField(rawInput, "email");
|
|
24
25
|
const password = input.password?.trim() || void 0;
|
|
25
26
|
const organizationId = readSetupNumberField(rawInput, "organizationId");
|
|
@@ -32,8 +33,7 @@ const omadeusSetupAdapter = {
|
|
|
32
33
|
omadeus: {
|
|
33
34
|
...omadeusPrevious,
|
|
34
35
|
enabled: true,
|
|
35
|
-
...
|
|
36
|
-
...maestroUrl ? { maestroUrl } : {},
|
|
36
|
+
...environment ? { environment } : {},
|
|
37
37
|
...email ? { email } : {},
|
|
38
38
|
...password ? { password } : {},
|
|
39
39
|
...organizationId ? { organizationId } : {}
|
package/openclaw.plugin.json
CHANGED
|
@@ -6,12 +6,18 @@
|
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"properties": {
|
|
8
8
|
"enabled": { "type": "boolean" },
|
|
9
|
-
"
|
|
10
|
-
|
|
9
|
+
"environment": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["production", "staging", "dev"]
|
|
12
|
+
},
|
|
11
13
|
"email": { "type": "string" },
|
|
12
14
|
"password": { "type": "string" },
|
|
13
15
|
"organizationId": { "type": "number" },
|
|
14
16
|
"sessionToken": { "type": "string" },
|
|
17
|
+
"sessionTokenEnvironment": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": ["production", "staging", "dev"]
|
|
20
|
+
},
|
|
15
21
|
"inbound": {
|
|
16
22
|
"type": "object",
|
|
17
23
|
"additionalProperties": false,
|
|
@@ -92,12 +98,18 @@
|
|
|
92
98
|
"additionalProperties": false,
|
|
93
99
|
"properties": {
|
|
94
100
|
"enabled": { "type": "boolean" },
|
|
95
|
-
"
|
|
96
|
-
|
|
101
|
+
"environment": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"enum": ["production", "staging", "dev"]
|
|
104
|
+
},
|
|
97
105
|
"email": { "type": "string" },
|
|
98
106
|
"password": { "type": "string" },
|
|
99
107
|
"organizationId": { "type": "number" },
|
|
100
108
|
"sessionToken": { "type": "string" },
|
|
109
|
+
"sessionTokenEnvironment": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"enum": ["production", "staging", "dev"]
|
|
112
|
+
},
|
|
101
113
|
"inbound": {
|
|
102
114
|
"type": "object",
|
|
103
115
|
"additionalProperties": false,
|
|
@@ -172,13 +184,8 @@
|
|
|
172
184
|
}
|
|
173
185
|
},
|
|
174
186
|
"uiHints": {
|
|
175
|
-
"
|
|
176
|
-
"label": "
|
|
177
|
-
"placeholder": "https://dev1-cas.rouztech.com"
|
|
178
|
-
},
|
|
179
|
-
"maestroUrl": {
|
|
180
|
-
"label": "Maestro URL",
|
|
181
|
-
"placeholder": "https://dev3-maestro.rouztech.com"
|
|
187
|
+
"environment": {
|
|
188
|
+
"label": "Environment"
|
|
182
189
|
},
|
|
183
190
|
"email": {
|
|
184
191
|
"label": "Email"
|
|
@@ -195,6 +202,10 @@
|
|
|
195
202
|
"sensitive": true,
|
|
196
203
|
"advanced": true
|
|
197
204
|
},
|
|
205
|
+
"sessionTokenEnvironment": {
|
|
206
|
+
"label": "Session token environment",
|
|
207
|
+
"advanced": true
|
|
208
|
+
},
|
|
198
209
|
"inbound": {
|
|
199
210
|
"label": "Inbound policy (Jaguar chat)",
|
|
200
211
|
"advanced": true
|
package/package.json
CHANGED
package/src/api/auth.api.ts
CHANGED
|
@@ -9,6 +9,27 @@ import type {
|
|
|
9
9
|
const CAS_APPLICATION_ID = 1;
|
|
10
10
|
const CAS_SCOPES = "title,email,avatar,firstName,lastName,birth,phone,countryCode";
|
|
11
11
|
|
|
12
|
+
function formatFetchError(label: string, url: string, method: string, err: unknown): Error {
|
|
13
|
+
const base = err instanceof Error ? err.message : String(err);
|
|
14
|
+
const cause =
|
|
15
|
+
err instanceof Error && err.cause instanceof Error ? err.cause.message : undefined;
|
|
16
|
+
const detail = cause && cause !== base ? `${base} (${cause})` : base;
|
|
17
|
+
return new Error(`${label} (${method} ${url}) failed: ${detail}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function omadeusFetch(
|
|
21
|
+
label: string,
|
|
22
|
+
url: string,
|
|
23
|
+
init: RequestInit,
|
|
24
|
+
): Promise<Response> {
|
|
25
|
+
const method = init.method ?? "GET";
|
|
26
|
+
try {
|
|
27
|
+
return await fetch(url, init);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
throw formatFetchError(label, url, method, err);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
12
33
|
export async function createCasToken(params: {
|
|
13
34
|
casUrl: string;
|
|
14
35
|
email: string;
|
|
@@ -17,11 +38,10 @@ export async function createCasToken(params: {
|
|
|
17
38
|
const { casUrl, email, password } = params;
|
|
18
39
|
const url = `${casUrl}/apiv1/tokens`;
|
|
19
40
|
const jsonBody = JSON.stringify({ email, password });
|
|
20
|
-
const res = await
|
|
41
|
+
const res = await omadeusFetch("CAS token request", url, {
|
|
21
42
|
method: "CREATE",
|
|
22
43
|
headers: {
|
|
23
44
|
"Content-Type": "application/json;charset=UTF-8",
|
|
24
|
-
"Content-Length": String(jsonBody.length),
|
|
25
45
|
},
|
|
26
46
|
body: jsonBody,
|
|
27
47
|
});
|
|
@@ -44,7 +64,7 @@ export async function getMe(params: {
|
|
|
44
64
|
}): Promise<{ email: string }> {
|
|
45
65
|
const { casUrl, casToken, refreshCookie } = params;
|
|
46
66
|
const url = `${casUrl}/apiv1/members/me`;
|
|
47
|
-
const res = await
|
|
67
|
+
const res = await omadeusFetch("CAS get member", url, {
|
|
48
68
|
method: "GET",
|
|
49
69
|
headers: {
|
|
50
70
|
Authorization: `Bearer ${casToken}`,
|
|
@@ -80,7 +100,7 @@ export async function createAuthorizationCode(params: {
|
|
|
80
100
|
Authorization: `Bearer ${token}`,
|
|
81
101
|
...(casSession?.refreshCookie ? { Cookie: casSession.refreshCookie } : {}),
|
|
82
102
|
};
|
|
83
|
-
const res = await
|
|
103
|
+
const res = await omadeusFetch("CAS authorization code request", url, {
|
|
84
104
|
method: "CREATE",
|
|
85
105
|
body,
|
|
86
106
|
headers,
|
|
@@ -104,7 +124,7 @@ export async function obtainSessionToken(params: {
|
|
|
104
124
|
}): Promise<string> {
|
|
105
125
|
const { maestroUrl, authorizationCode, organizationId } = params;
|
|
106
126
|
const url = `${maestroUrl}/dolphin/apiv1/oauth2/tokens`;
|
|
107
|
-
const res = await
|
|
127
|
+
const res = await omadeusFetch("Omadeus session token request", url, {
|
|
108
128
|
method: "OBTAIN",
|
|
109
129
|
headers: { "Content-Type": "application/json;charset=UTF-8" },
|
|
110
130
|
body: JSON.stringify({ authorizationCode, organizationId }),
|
|
@@ -126,7 +146,7 @@ export async function listOrganizations(params: {
|
|
|
126
146
|
}): Promise<OmadeusOrganization[]> {
|
|
127
147
|
const { maestroUrl, email } = params;
|
|
128
148
|
const url = `${maestroUrl}/dolphin/apiv1/organizations`;
|
|
129
|
-
const res = await
|
|
149
|
+
const res = await omadeusFetch("Omadeus list organizations", url, {
|
|
130
150
|
method: "LIST",
|
|
131
151
|
headers: { "Content-Type": "application/json;charset=UTF-8" },
|
|
132
152
|
body: JSON.stringify({ email }),
|
|
@@ -145,7 +165,7 @@ export async function listOrganizationMembers(params: {
|
|
|
145
165
|
}): Promise<OmadeusOrganizationMember[]> {
|
|
146
166
|
const { maestroUrl, sessionToken, organizationId } = params;
|
|
147
167
|
const url = `${maestroUrl}/dolphin/apiv1/organizations/${organizationId}/members`;
|
|
148
|
-
const res = await
|
|
168
|
+
const res = await omadeusFetch("Omadeus list organization members", url, {
|
|
149
169
|
method: "LIST",
|
|
150
170
|
headers: {
|
|
151
171
|
Authorization: `Bearer ${sessionToken}`,
|
package/src/channel.ts
CHANGED
|
@@ -57,12 +57,15 @@ const isUnconfigured = (account: Account) => account.credentialSource === "none"
|
|
|
57
57
|
|
|
58
58
|
let lastPersistedToken: string | null = null;
|
|
59
59
|
|
|
60
|
-
async function persistSessionToken(
|
|
60
|
+
async function persistSessionToken(
|
|
61
|
+
token: string,
|
|
62
|
+
environment: Account["environment"],
|
|
63
|
+
): Promise<void> {
|
|
61
64
|
if (lastPersistedToken === token) return;
|
|
62
65
|
const runtime = getOmadeusRuntime();
|
|
63
66
|
const cfg = runtime.config.current() as OpenClawConfig;
|
|
64
67
|
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
65
|
-
if (section.sessionToken === token) {
|
|
68
|
+
if (section.sessionToken === token && section.sessionTokenEnvironment === environment) {
|
|
66
69
|
lastPersistedToken = token;
|
|
67
70
|
return;
|
|
68
71
|
}
|
|
@@ -74,6 +77,7 @@ async function persistSessionToken(token: string): Promise<void> {
|
|
|
74
77
|
omadeus: {
|
|
75
78
|
...(getOmadeusChannelConfig(draft) ?? {}),
|
|
76
79
|
sessionToken: token,
|
|
80
|
+
sessionTokenEnvironment: environment,
|
|
77
81
|
},
|
|
78
82
|
};
|
|
79
83
|
},
|
|
@@ -103,12 +107,12 @@ const omadeusConfigAdapter = createTopLevelChannelConfigAdapter<Account>({
|
|
|
103
107
|
defaultAccountId: resolveDefaultOmadeusAccountId,
|
|
104
108
|
deleteMode: "clear-fields",
|
|
105
109
|
clearBaseFields: [
|
|
106
|
-
"
|
|
107
|
-
"maestroUrl",
|
|
110
|
+
"environment",
|
|
108
111
|
"email",
|
|
109
112
|
"password",
|
|
110
113
|
"organizationId",
|
|
111
114
|
"sessionToken",
|
|
115
|
+
"sessionTokenEnvironment",
|
|
112
116
|
"inbound",
|
|
113
117
|
],
|
|
114
118
|
// Keep adapter contract satisfied even though Omadeus no longer uses DM allowlists.
|
|
@@ -561,7 +565,7 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
561
565
|
initialToken: account.sessionToken,
|
|
562
566
|
onRefresh: (token) => {
|
|
563
567
|
log.info("[omadeus] token refreshed");
|
|
564
|
-
void persistSessionToken(token).catch((err) =>
|
|
568
|
+
void persistSessionToken(token, account.environment).catch((err) =>
|
|
565
569
|
log.warn(`[omadeus] failed to persist session token: ${String(err)}`),
|
|
566
570
|
);
|
|
567
571
|
},
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_ACCOUNT_ID, type OpenClawConfig } from "../runtime-api.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getOmadeusEnvironmentUrls,
|
|
4
|
+
resolveOmadeusEnvironment,
|
|
5
|
+
} from "./defaults.js";
|
|
3
6
|
import type { OmadeusChannelConfig, ResolvedOmadeusAccount } from "./types.js";
|
|
4
7
|
|
|
5
8
|
export function getOmadeusChannelConfig(cfg: OpenClawConfig): OmadeusChannelConfig | undefined {
|
|
@@ -24,11 +27,17 @@ export function resolveOmadeusAccount(params: {
|
|
|
24
27
|
}): ResolvedOmadeusAccount {
|
|
25
28
|
const { cfg } = params;
|
|
26
29
|
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
30
|
+
const environment = resolveOmadeusEnvironment(section.environment);
|
|
31
|
+
const { casUrl, maestroUrl } = getOmadeusEnvironmentUrls(environment);
|
|
27
32
|
const envCredentials = resolveOmadeusEnvCredentials();
|
|
28
33
|
const email = section.email?.trim() || envCredentials?.email || "";
|
|
29
34
|
const password = section.password?.trim() || envCredentials?.password || "";
|
|
30
35
|
const orgId = section.organizationId ?? envCredentials?.organizationId;
|
|
31
|
-
const
|
|
36
|
+
const rawSessionToken = section.sessionToken?.trim() ?? "";
|
|
37
|
+
const sessionTokenEnvironment = resolveOmadeusEnvironment(section.sessionTokenEnvironment);
|
|
38
|
+
const sessionTokenValid =
|
|
39
|
+
Boolean(rawSessionToken) && sessionTokenEnvironment === environment;
|
|
40
|
+
const sessionToken = sessionTokenValid ? rawSessionToken : "";
|
|
32
41
|
const hasCredentials = Boolean(email && password && orgId);
|
|
33
42
|
const hasSessionToken = Boolean(sessionToken);
|
|
34
43
|
const hasConfigCredentials = Boolean(
|
|
@@ -47,8 +56,9 @@ export function resolveOmadeusAccount(params: {
|
|
|
47
56
|
name: "Omadeus",
|
|
48
57
|
enabled: section.enabled !== false,
|
|
49
58
|
config: section,
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
environment,
|
|
60
|
+
casUrl,
|
|
61
|
+
maestroUrl,
|
|
52
62
|
email,
|
|
53
63
|
password,
|
|
54
64
|
organizationId: orgId ?? 0,
|
package/src/defaults.ts
CHANGED
|
@@ -1,2 +1,44 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
1
|
+
export type OmadeusEnvironment = "production" | "staging" | "dev";
|
|
2
|
+
|
|
3
|
+
export const OMADEUS_DEFAULT_ENVIRONMENT: OmadeusEnvironment = "dev";
|
|
4
|
+
|
|
5
|
+
export type OmadeusEnvironmentConfig = {
|
|
6
|
+
label: string;
|
|
7
|
+
casUrl: string;
|
|
8
|
+
maestroUrl: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const OMADEUS_ENVIRONMENTS: Record<OmadeusEnvironment, OmadeusEnvironmentConfig> = {
|
|
12
|
+
production: {
|
|
13
|
+
label: "Production",
|
|
14
|
+
casUrl: "https://xas.xeba.tech",
|
|
15
|
+
maestroUrl: "https://maestro.xeba.tech",
|
|
16
|
+
},
|
|
17
|
+
staging: {
|
|
18
|
+
label: "Staging",
|
|
19
|
+
casUrl: "https://staging-xas.xeba.tech",
|
|
20
|
+
maestroUrl: "https://staging.xeba.tech",
|
|
21
|
+
},
|
|
22
|
+
dev: {
|
|
23
|
+
label: "Dev",
|
|
24
|
+
casUrl: "https://dev1-cas.rouztech.com",
|
|
25
|
+
maestroUrl: "https://dev1-maestro.rouztech.com",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const OMADEUS_ENVIRONMENT_SET = new Set<string>(Object.keys(OMADEUS_ENVIRONMENTS));
|
|
30
|
+
|
|
31
|
+
export function resolveOmadeusEnvironment(value: unknown): OmadeusEnvironment {
|
|
32
|
+
if (typeof value === "string" && OMADEUS_ENVIRONMENT_SET.has(value)) {
|
|
33
|
+
return value as OmadeusEnvironment;
|
|
34
|
+
}
|
|
35
|
+
return OMADEUS_DEFAULT_ENVIRONMENT;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getOmadeusEnvironmentUrls(env: OmadeusEnvironment): {
|
|
39
|
+
casUrl: string;
|
|
40
|
+
maestroUrl: string;
|
|
41
|
+
} {
|
|
42
|
+
const config = OMADEUS_ENVIRONMENTS[env];
|
|
43
|
+
return { casUrl: config.casUrl, maestroUrl: config.maestroUrl };
|
|
44
|
+
}
|
package/src/onboarding.ts
CHANGED
|
@@ -7,7 +7,13 @@ import {
|
|
|
7
7
|
import { listMemberChannelViews } from "./api/channel.api.js";
|
|
8
8
|
import { authenticate } from "./auth.js";
|
|
9
9
|
import { getOmadeusChannelConfig, resolveOmadeusAccount } from "./config.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getOmadeusEnvironmentUrls,
|
|
12
|
+
OMADEUS_DEFAULT_ENVIRONMENT,
|
|
13
|
+
OMADEUS_ENVIRONMENTS,
|
|
14
|
+
resolveOmadeusEnvironment,
|
|
15
|
+
type OmadeusEnvironment,
|
|
16
|
+
} from "./defaults.js";
|
|
11
17
|
import { formatMemberLabel } from "./member-resolve.js";
|
|
12
18
|
import type {
|
|
13
19
|
OmadeusChannelConfig,
|
|
@@ -18,7 +24,6 @@ import type {
|
|
|
18
24
|
import { OMADEUS_INBOUND_ENTITY_KINDS } from "./types.js";
|
|
19
25
|
|
|
20
26
|
const channel = "omadeus" as const;
|
|
21
|
-
const DONE = "__done__";
|
|
22
27
|
|
|
23
28
|
type SelectOption = {
|
|
24
29
|
value: string;
|
|
@@ -26,33 +31,54 @@ type SelectOption = {
|
|
|
26
31
|
hint?: string;
|
|
27
32
|
};
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
34
|
+
function formatAuthError(err: unknown): string {
|
|
35
|
+
if (!(err instanceof Error)) return String(err);
|
|
36
|
+
const parts = [err.message];
|
|
37
|
+
const { cause } = err;
|
|
38
|
+
if (cause instanceof Error) {
|
|
39
|
+
parts.push(cause.message);
|
|
40
|
+
const code = (cause as Error & { code?: unknown }).code;
|
|
41
|
+
if (typeof code === "string" && code) {
|
|
42
|
+
parts.push(`(${code})`);
|
|
43
|
+
}
|
|
44
|
+
} else if (typeof cause === "string" && cause.trim()) {
|
|
45
|
+
parts.push(cause);
|
|
46
|
+
}
|
|
47
|
+
return parts.join(" — ");
|
|
48
|
+
}
|
|
40
49
|
|
|
41
|
-
async function noteOmadeusAuthHelp(
|
|
50
|
+
async function noteOmadeusAuthHelp(
|
|
51
|
+
prompter: WizardPrompter,
|
|
52
|
+
environment: OmadeusEnvironment,
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
const envLabel = OMADEUS_ENVIRONMENTS[environment].label;
|
|
42
55
|
await prompter.note(
|
|
43
56
|
[
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
" - Organization ID (we can look it up for you)",
|
|
48
|
-
`CAS URL: ${OMADEUS_CAS_URL}`,
|
|
49
|
-
`Maestro URL: ${OMADEUS_MAESTRO_URL}`,
|
|
50
|
-
"Env vars supported: OMADEUS_EMAIL, OMADEUS_PASSWORD, OMADEUS_ORGANIZATION_ID.",
|
|
57
|
+
`Connect OpenClaw to Omadeus (${envLabel}).`,
|
|
58
|
+
"",
|
|
59
|
+
"We'll ask for your email and password, then show the organizations on your account so you can pick one.",
|
|
51
60
|
].join("\n"),
|
|
52
61
|
"Omadeus setup",
|
|
53
62
|
);
|
|
54
63
|
}
|
|
55
64
|
|
|
65
|
+
async function promptEnvironment(
|
|
66
|
+
prompter: WizardPrompter,
|
|
67
|
+
existing?: OmadeusEnvironment,
|
|
68
|
+
): Promise<OmadeusEnvironment> {
|
|
69
|
+
const initial = existing ?? OMADEUS_DEFAULT_ENVIRONMENT;
|
|
70
|
+
const choice = await prompter.select({
|
|
71
|
+
message: "Select Omadeus environment",
|
|
72
|
+
options: (Object.keys(OMADEUS_ENVIRONMENTS) as OmadeusEnvironment[]).map((env) => ({
|
|
73
|
+
value: env,
|
|
74
|
+
label: OMADEUS_ENVIRONMENTS[env].label,
|
|
75
|
+
hint: getOmadeusEnvironmentUrls(env).maestroUrl,
|
|
76
|
+
})),
|
|
77
|
+
initialValue: initial,
|
|
78
|
+
});
|
|
79
|
+
return resolveOmadeusEnvironment(choice);
|
|
80
|
+
}
|
|
81
|
+
|
|
56
82
|
async function promptOrganizationId(params: {
|
|
57
83
|
prompter: WizardPrompter;
|
|
58
84
|
maestroUrl: string;
|
|
@@ -118,8 +144,21 @@ async function promptChannelSelection(params: {
|
|
|
118
144
|
take: 100,
|
|
119
145
|
});
|
|
120
146
|
if (channels.length === 0) {
|
|
121
|
-
|
|
147
|
+
await prompter.note(
|
|
148
|
+
"No channels found for this account. Channel listening will stay disabled.",
|
|
149
|
+
"Omadeus channels",
|
|
150
|
+
);
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const listenToChannels = await prompter.confirm({
|
|
155
|
+
message: "Listen for messages in Omadeus channels?",
|
|
156
|
+
initialValue: (existingChannelViewIds?.length ?? 0) > 0,
|
|
157
|
+
});
|
|
158
|
+
if (!listenToChannels) {
|
|
159
|
+
return [];
|
|
122
160
|
}
|
|
161
|
+
|
|
123
162
|
const selected = await promptMultiSelect({
|
|
124
163
|
prompter,
|
|
125
164
|
message: "Which channels should OpenClaw listen to?",
|
|
@@ -133,13 +172,9 @@ async function promptChannelSelection(params: {
|
|
|
133
172
|
initialValues:
|
|
134
173
|
existingChannelViewIds && existingChannelViewIds.length > 0
|
|
135
174
|
? existingChannelViewIds.map(String)
|
|
136
|
-
:
|
|
175
|
+
: undefined,
|
|
137
176
|
});
|
|
138
|
-
|
|
139
|
-
if (chosen.length === 0) {
|
|
140
|
-
throw new Error("At least one channel must be selected.");
|
|
141
|
-
}
|
|
142
|
-
return chosen;
|
|
177
|
+
return channels.filter((item) => selected.includes(String(item.id)));
|
|
143
178
|
}
|
|
144
179
|
|
|
145
180
|
function memberHint(member: OmadeusOrganizationMember): string | undefined {
|
|
@@ -154,40 +189,11 @@ async function promptMultiSelect(params: {
|
|
|
154
189
|
options: SelectOption[];
|
|
155
190
|
initialValues?: string[];
|
|
156
191
|
}): Promise<string[]> {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
options: params.options,
|
|
163
|
-
initialValues: params.initialValues,
|
|
164
|
-
initialValue: params.initialValues,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const selected = new Set(params.initialValues ?? []);
|
|
169
|
-
while (true) {
|
|
170
|
-
const next = await params.prompter.select({
|
|
171
|
-
message: `${params.message} (${selected.size} selected)`,
|
|
172
|
-
options: [
|
|
173
|
-
{ value: DONE, label: selected.size > 0 ? "Done" : "Done (select none)" },
|
|
174
|
-
...params.options.map((option) => ({
|
|
175
|
-
...option,
|
|
176
|
-
label: selected.has(option.value) ? `[selected] ${option.label}` : option.label,
|
|
177
|
-
})),
|
|
178
|
-
],
|
|
179
|
-
initialValue: DONE,
|
|
180
|
-
});
|
|
181
|
-
const value = String(next);
|
|
182
|
-
if (value === DONE) {
|
|
183
|
-
return [...selected];
|
|
184
|
-
}
|
|
185
|
-
if (selected.has(value)) {
|
|
186
|
-
selected.delete(value);
|
|
187
|
-
} else {
|
|
188
|
-
selected.add(value);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
192
|
+
return params.prompter.multiselect({
|
|
193
|
+
message: params.message,
|
|
194
|
+
options: params.options,
|
|
195
|
+
initialValues: params.initialValues,
|
|
196
|
+
});
|
|
191
197
|
}
|
|
192
198
|
|
|
193
199
|
async function loadSelectableMembers(params: {
|
|
@@ -332,16 +338,19 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
332
338
|
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
333
339
|
let next = cfg;
|
|
334
340
|
|
|
341
|
+
const environment = await promptEnvironment(
|
|
342
|
+
prompter,
|
|
343
|
+
section.environment ? resolveOmadeusEnvironment(section.environment) : undefined,
|
|
344
|
+
);
|
|
345
|
+
const { casUrl, maestroUrl } = getOmadeusEnvironmentUrls(environment);
|
|
346
|
+
|
|
335
347
|
if (account.credentialSource === "none") {
|
|
336
|
-
await noteOmadeusAuthHelp(prompter);
|
|
348
|
+
await noteOmadeusAuthHelp(prompter, environment);
|
|
337
349
|
}
|
|
338
350
|
|
|
339
351
|
const envEmail = process.env.OMADEUS_EMAIL?.trim();
|
|
340
352
|
const envPassword = process.env.OMADEUS_PASSWORD?.trim();
|
|
341
353
|
|
|
342
|
-
const casUrl = OMADEUS_CAS_URL;
|
|
343
|
-
const maestroUrl = OMADEUS_MAESTRO_URL;
|
|
344
|
-
|
|
345
354
|
let { email, password } = await promptCredentials(prompter, {
|
|
346
355
|
email: section.email ?? envEmail,
|
|
347
356
|
password: section.password ?? envPassword,
|
|
@@ -370,8 +379,10 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
370
379
|
await prompter.note(`Authenticated as ${payload.email}`, "Omadeus authentication");
|
|
371
380
|
break;
|
|
372
381
|
} catch (err) {
|
|
373
|
-
|
|
374
|
-
|
|
382
|
+
await prompter.note(
|
|
383
|
+
`Authentication failed: ${formatAuthError(err)}`,
|
|
384
|
+
"Omadeus authentication",
|
|
385
|
+
);
|
|
375
386
|
const retry = await prompter.confirm({
|
|
376
387
|
message: "Re-enter email/password and try again?",
|
|
377
388
|
initialValue: true,
|
|
@@ -418,12 +429,15 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
418
429
|
existingChannelViewIds: existingInbound?.channels?.allowedChannelViewIds,
|
|
419
430
|
});
|
|
420
431
|
|
|
421
|
-
const channelSenderIds =
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
432
|
+
const channelSenderIds =
|
|
433
|
+
selectedChannels.length > 0
|
|
434
|
+
? await promptSenderAllowlist({
|
|
435
|
+
prompter,
|
|
436
|
+
message: "Which users can trigger OpenClaw from allowed channels?",
|
|
437
|
+
members,
|
|
438
|
+
existingReferenceIds: existingInbound?.channels?.allowedSenderReferenceIds,
|
|
439
|
+
})
|
|
440
|
+
: undefined;
|
|
427
441
|
|
|
428
442
|
const entityKinds = await promptEntityKindSelection({
|
|
429
443
|
prompter,
|
|
@@ -456,11 +470,16 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
456
470
|
const entityKindSummary =
|
|
457
471
|
entityKinds.length > 0 ? entityKinds.join(", ") : "none (entity rooms disabled)";
|
|
458
472
|
|
|
473
|
+
const channelSummary =
|
|
474
|
+
selectedChannels.length > 0
|
|
475
|
+
? `- Channels "${channelTitles}": rooms ${channelRoomIds.join(", ") || "(no room ids)"} from ${senderSummary(channelSenderIds)}; @mention not required in those rooms.`
|
|
476
|
+
: "- Channels: disabled (none selected).";
|
|
477
|
+
|
|
459
478
|
await prompter.note(
|
|
460
479
|
[
|
|
461
480
|
`Inbound policy (Jaguar chat):`,
|
|
462
481
|
`- Direct messages: enabled for ${senderSummary(directSenderIds)} (no @mention required).`,
|
|
463
|
-
|
|
482
|
+
channelSummary,
|
|
464
483
|
`- Entity rooms (${entityKindSummary}): ${senderSummary(entitySenderIds)}; @mention required.`,
|
|
465
484
|
].join("\n"),
|
|
466
485
|
"Omadeus inbound policy",
|
|
@@ -472,12 +491,12 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
472
491
|
...next.channels,
|
|
473
492
|
omadeus: {
|
|
474
493
|
enabled: true,
|
|
475
|
-
|
|
476
|
-
maestroUrl,
|
|
494
|
+
environment,
|
|
477
495
|
email,
|
|
478
496
|
password,
|
|
479
497
|
organizationId,
|
|
480
498
|
sessionToken,
|
|
499
|
+
sessionTokenEnvironment: environment,
|
|
481
500
|
inbound: {
|
|
482
501
|
version: 1,
|
|
483
502
|
direct: {
|
|
@@ -486,7 +505,7 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
486
505
|
requireMention: "never",
|
|
487
506
|
},
|
|
488
507
|
channels: {
|
|
489
|
-
enabled:
|
|
508
|
+
enabled: selectedChannels.length > 0,
|
|
490
509
|
allowedRoomIds: channelRoomIds,
|
|
491
510
|
allowedChannelViewIds: channelViewIds,
|
|
492
511
|
...(channelSenderIds ? { allowedSenderReferenceIds: channelSenderIds } : {}),
|
package/src/setup-core.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ChannelSetupAdapter } from "openclaw/plugin-sdk/setup";
|
|
2
2
|
import type { OpenClawConfig } from "../runtime-api.js";
|
|
3
|
+
import { resolveOmadeusEnvironment } from "./defaults.js";
|
|
3
4
|
|
|
4
5
|
function readSetupStringField(input: Record<string, unknown>, key: string): string | undefined {
|
|
5
6
|
const value = input[key];
|
|
@@ -29,8 +30,8 @@ export const omadeusSetupAdapter: ChannelSetupAdapter = {
|
|
|
29
30
|
},
|
|
30
31
|
applyAccountConfig: ({ cfg, input }) => {
|
|
31
32
|
const rawInput = input as Record<string, unknown>;
|
|
32
|
-
const
|
|
33
|
-
const
|
|
33
|
+
const environmentRaw = readSetupStringField(rawInput, "environment");
|
|
34
|
+
const environment = environmentRaw ? resolveOmadeusEnvironment(environmentRaw) : undefined;
|
|
34
35
|
const email = readSetupStringField(rawInput, "email");
|
|
35
36
|
const password = input.password?.trim() || undefined;
|
|
36
37
|
const organizationId = readSetupNumberField(rawInput, "organizationId");
|
|
@@ -51,8 +52,7 @@ export const omadeusSetupAdapter: ChannelSetupAdapter = {
|
|
|
51
52
|
omadeus: {
|
|
52
53
|
...omadeusPrevious,
|
|
53
54
|
enabled: true,
|
|
54
|
-
...(
|
|
55
|
-
...(maestroUrl ? { maestroUrl } : {}),
|
|
55
|
+
...(environment ? { environment } : {}),
|
|
56
56
|
...(email ? { email } : {}),
|
|
57
57
|
...(password ? { password } : {}),
|
|
58
58
|
...(organizationId ? { organizationId } : {}),
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { OmadeusEnvironment } from "./defaults.js";
|
|
2
|
+
|
|
1
3
|
// ---------------------------------------------------------------------------
|
|
2
4
|
// Omadeus config shape (stored under channels.omadeus in OpenClaw config)
|
|
3
5
|
// ---------------------------------------------------------------------------
|
|
@@ -61,13 +63,14 @@ export type OmadeusInboundPolicy = {
|
|
|
61
63
|
|
|
62
64
|
export type OmadeusChannelConfig = {
|
|
63
65
|
enabled?: boolean;
|
|
64
|
-
|
|
65
|
-
maestroUrl?: string;
|
|
66
|
+
environment?: OmadeusEnvironment;
|
|
66
67
|
email?: string;
|
|
67
68
|
password?: string;
|
|
68
69
|
organizationId?: number;
|
|
69
70
|
/** Cached Omadeus session JWT obtained during onboarding/startup. */
|
|
70
71
|
sessionToken?: string;
|
|
72
|
+
/** Environment the cached sessionToken was minted under (must match `environment`). */
|
|
73
|
+
sessionTokenEnvironment?: OmadeusEnvironment;
|
|
71
74
|
/** Jaguar chat ingress allowlists and mention rules. */
|
|
72
75
|
inbound?: OmadeusInboundPolicy;
|
|
73
76
|
};
|
|
@@ -77,6 +80,7 @@ export type ResolvedOmadeusAccount = {
|
|
|
77
80
|
name?: string;
|
|
78
81
|
enabled: boolean;
|
|
79
82
|
config: OmadeusChannelConfig;
|
|
83
|
+
environment: OmadeusEnvironment;
|
|
80
84
|
casUrl: string;
|
|
81
85
|
maestroUrl: string;
|
|
82
86
|
email: string;
|