@kanvas/openclaw-plugin 0.1.14 → 0.1.16
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/dist/domains/crm/index.js +1 -1
- package/dist/domains/ecosystem/index.js +167 -0
- package/dist/domains/social/index.js +12 -8
- package/dist/index.js +18 -0
- package/dist/tools/ecosystem.js +145 -0
- package/package.json +1 -1
|
@@ -396,7 +396,7 @@ export class CrmService {
|
|
|
396
396
|
}
|
|
397
397
|
async listLeadMessages(channelSlug, first = 50, page = 1) {
|
|
398
398
|
const query = `
|
|
399
|
-
query LeadMessages($first: Int, $page: Int, $channelSlug:
|
|
399
|
+
query LeadMessages($first: Int, $page: Int, $channelSlug: Mixed!) {
|
|
400
400
|
messages(
|
|
401
401
|
first: $first
|
|
402
402
|
page: $page
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export class EcosystemService {
|
|
2
|
+
client;
|
|
3
|
+
constructor(client) {
|
|
4
|
+
this.client = client;
|
|
5
|
+
}
|
|
6
|
+
// ── Companies ──────────────────────────────────────────────
|
|
7
|
+
async listCompanies(first = 25, search, where) {
|
|
8
|
+
const query = `
|
|
9
|
+
query ListCompanies($first: Int, $search: String, $where: QueryCompaniesWhereWhereConditions) {
|
|
10
|
+
companies(first: $first, search: $search, where: $where) {
|
|
11
|
+
data {
|
|
12
|
+
id
|
|
13
|
+
uuid
|
|
14
|
+
name
|
|
15
|
+
website
|
|
16
|
+
email
|
|
17
|
+
phone
|
|
18
|
+
address
|
|
19
|
+
city
|
|
20
|
+
state
|
|
21
|
+
country
|
|
22
|
+
zip
|
|
23
|
+
is_active
|
|
24
|
+
total_users
|
|
25
|
+
total_branches
|
|
26
|
+
created_at
|
|
27
|
+
}
|
|
28
|
+
paginatorInfo {
|
|
29
|
+
currentPage
|
|
30
|
+
lastPage
|
|
31
|
+
total
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
return this.client.query(query, { first, search, where });
|
|
37
|
+
}
|
|
38
|
+
// ── Branches ───────────────────────────────────────────────
|
|
39
|
+
async listBranches(first = 25, search, where) {
|
|
40
|
+
const query = `
|
|
41
|
+
query ListBranches($first: Int, $search: String, $where: QueryBranchesWhereWhereConditions) {
|
|
42
|
+
branches(first: $first, search: $search, where: $where) {
|
|
43
|
+
data {
|
|
44
|
+
id
|
|
45
|
+
uuid
|
|
46
|
+
name
|
|
47
|
+
companies_id
|
|
48
|
+
email
|
|
49
|
+
phone
|
|
50
|
+
address
|
|
51
|
+
city
|
|
52
|
+
state
|
|
53
|
+
country
|
|
54
|
+
zip
|
|
55
|
+
is_default
|
|
56
|
+
is_active
|
|
57
|
+
total_users
|
|
58
|
+
created_at
|
|
59
|
+
}
|
|
60
|
+
paginatorInfo {
|
|
61
|
+
currentPage
|
|
62
|
+
lastPage
|
|
63
|
+
total
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
return this.client.query(query, { first, search, where });
|
|
69
|
+
}
|
|
70
|
+
// ── Roles ──────────────────────────────────────────────────
|
|
71
|
+
async listRoles(first = 50, search) {
|
|
72
|
+
const query = `
|
|
73
|
+
query ListRoles($first: Int, $search: String) {
|
|
74
|
+
roles(first: $first, search: $search) {
|
|
75
|
+
data {
|
|
76
|
+
id
|
|
77
|
+
name
|
|
78
|
+
title
|
|
79
|
+
scope
|
|
80
|
+
userCount
|
|
81
|
+
systemRole
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
return this.client.query(query, { first, search });
|
|
87
|
+
}
|
|
88
|
+
// ── Company Users ──────────────────────────────────────────
|
|
89
|
+
async listCompanyUsers(first = 25, search, where) {
|
|
90
|
+
const query = `
|
|
91
|
+
query ListCompanyUsers($first: Int, $search: String, $where: QueryCompanyUsersWhereWhereConditions) {
|
|
92
|
+
companyUsers(first: $first, search: $search, where: $where) {
|
|
93
|
+
data {
|
|
94
|
+
id
|
|
95
|
+
uuid
|
|
96
|
+
firstname
|
|
97
|
+
lastname
|
|
98
|
+
email
|
|
99
|
+
displayname
|
|
100
|
+
roles
|
|
101
|
+
}
|
|
102
|
+
paginatorInfo {
|
|
103
|
+
currentPage
|
|
104
|
+
lastPage
|
|
105
|
+
total
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
return this.client.query(query, { first, search, where });
|
|
111
|
+
}
|
|
112
|
+
// ── User-Company Management ────────────────────────────────
|
|
113
|
+
async addUserToCompany(companyId, userId, roleId) {
|
|
114
|
+
const mutation = `
|
|
115
|
+
mutation AddUserToCompany($id: ID!, $user_id: ID!, $rol_id: ID) {
|
|
116
|
+
addUserToCompany(id: $id, user_id: $user_id, rol_id: $rol_id)
|
|
117
|
+
}
|
|
118
|
+
`;
|
|
119
|
+
return this.client.query(mutation, {
|
|
120
|
+
id: companyId,
|
|
121
|
+
user_id: userId,
|
|
122
|
+
rol_id: roleId,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async removeUserFromCompany(companyId, userId) {
|
|
126
|
+
const mutation = `
|
|
127
|
+
mutation RemoveUserFromCompany($id: ID!, $user_id: ID!) {
|
|
128
|
+
removeUserFromCompany(id: $id, user_id: $user_id)
|
|
129
|
+
}
|
|
130
|
+
`;
|
|
131
|
+
return this.client.query(mutation, { id: companyId, user_id: userId });
|
|
132
|
+
}
|
|
133
|
+
// ── User-Branch Management ─────────────────────────────────
|
|
134
|
+
async addUserToBranch(branchId, userId) {
|
|
135
|
+
const mutation = `
|
|
136
|
+
mutation AddUserToBranch($id: ID!, $user_id: ID!) {
|
|
137
|
+
addUserToBranch(id: $id, user_id: $user_id)
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
return this.client.query(mutation, { id: branchId, user_id: userId });
|
|
141
|
+
}
|
|
142
|
+
async removeUserFromBranch(branchId, userId) {
|
|
143
|
+
const mutation = `
|
|
144
|
+
mutation RemoveUserFromBranch($id: ID!, $user_id: ID!) {
|
|
145
|
+
removeUserFromBranch(id: $id, user_id: $user_id)
|
|
146
|
+
}
|
|
147
|
+
`;
|
|
148
|
+
return this.client.query(mutation, { id: branchId, user_id: userId });
|
|
149
|
+
}
|
|
150
|
+
// ── Role Assignment ────────────────────────────────────────
|
|
151
|
+
async assignRoleToUser(userId, roleIds) {
|
|
152
|
+
const mutation = `
|
|
153
|
+
mutation AssignRoleToUser($userId: ID!, $roleIds: [ID!]!) {
|
|
154
|
+
assignRoleToUser(userId: $userId, roleIds: $roleIds)
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
return this.client.query(mutation, { userId, roleIds });
|
|
158
|
+
}
|
|
159
|
+
async removeRoleFromUser(userId, role) {
|
|
160
|
+
const mutation = `
|
|
161
|
+
mutation RemoveRole($userId: ID!, $role: Mixed!) {
|
|
162
|
+
removeRole(userId: $userId, role: $role)
|
|
163
|
+
}
|
|
164
|
+
`;
|
|
165
|
+
return this.client.query(mutation, { userId, role });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -39,8 +39,10 @@ export class SocialService {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
custom_fields {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
data {
|
|
43
|
+
name
|
|
44
|
+
value
|
|
45
|
+
}
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
}
|
|
@@ -70,7 +72,6 @@ export class SocialService {
|
|
|
70
72
|
is_public
|
|
71
73
|
is_locked
|
|
72
74
|
created_at
|
|
73
|
-
updated_at
|
|
74
75
|
parent {
|
|
75
76
|
id
|
|
76
77
|
uuid
|
|
@@ -126,8 +127,10 @@ export class SocialService {
|
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
129
|
custom_fields {
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
data {
|
|
131
|
+
name
|
|
132
|
+
value
|
|
133
|
+
}
|
|
131
134
|
}
|
|
132
135
|
}
|
|
133
136
|
}
|
|
@@ -147,7 +150,6 @@ export class SocialService {
|
|
|
147
150
|
message
|
|
148
151
|
is_public
|
|
149
152
|
is_locked
|
|
150
|
-
updated_at
|
|
151
153
|
messageType {
|
|
152
154
|
id
|
|
153
155
|
name
|
|
@@ -160,8 +162,10 @@ export class SocialService {
|
|
|
160
162
|
}
|
|
161
163
|
}
|
|
162
164
|
custom_fields {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
data {
|
|
166
|
+
name
|
|
167
|
+
value
|
|
168
|
+
}
|
|
165
169
|
}
|
|
166
170
|
}
|
|
167
171
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,12 @@ import { CrmService } from "./domains/crm/index.js";
|
|
|
4
4
|
import { InventoryService } from "./domains/inventory/index.js";
|
|
5
5
|
import { OrdersService } from "./domains/orders/index.js";
|
|
6
6
|
import { SocialService } from "./domains/social/index.js";
|
|
7
|
+
import { EcosystemService } from "./domains/ecosystem/index.js";
|
|
7
8
|
import { registerCrmTools } from "./tools/crm.js";
|
|
8
9
|
import { registerInventoryTools } from "./tools/inventory.js";
|
|
9
10
|
import { registerOrdersTools } from "./tools/orders.js";
|
|
10
11
|
import { registerSocialTools } from "./tools/social.js";
|
|
12
|
+
import { registerEcosystemTools } from "./tools/ecosystem.js";
|
|
11
13
|
import { toolResult } from "./tools/helpers.js";
|
|
12
14
|
const DEFAULT_API_URL = "https://graphapi.kanvas.dev/graphql";
|
|
13
15
|
// OpenClaw v2026.4.5+ calls register() per-agent-context (main, subagents,
|
|
@@ -20,6 +22,7 @@ let sharedCrm = null;
|
|
|
20
22
|
let sharedInventory = null;
|
|
21
23
|
let sharedOrders = null;
|
|
22
24
|
let sharedSocial = null;
|
|
25
|
+
let sharedEcosystem = null;
|
|
23
26
|
let startupBannerShown = false;
|
|
24
27
|
let skipBannerShown = false;
|
|
25
28
|
function resolveConfig(pluginConfig) {
|
|
@@ -111,6 +114,7 @@ export default {
|
|
|
111
114
|
sharedInventory = new InventoryService(sharedClient);
|
|
112
115
|
sharedOrders = new OrdersService(sharedClient);
|
|
113
116
|
sharedSocial = new SocialService(sharedClient);
|
|
117
|
+
sharedEcosystem = new EcosystemService(sharedClient);
|
|
114
118
|
}
|
|
115
119
|
// Tools and hooks must be registered on every api object — each one is
|
|
116
120
|
// a separate agent context (main, subagents, cron lanes).
|
|
@@ -119,6 +123,7 @@ export default {
|
|
|
119
123
|
registerInventoryTools(api, sharedInventory, ensureAuth);
|
|
120
124
|
registerOrdersTools(api, sharedOrders, ensureAuth);
|
|
121
125
|
registerSocialTools(api, sharedSocial, ensureAuth);
|
|
126
|
+
registerEcosystemTools(api, sharedEcosystem, ensureAuth);
|
|
122
127
|
api.registerTool({
|
|
123
128
|
name: "kanvas_test_connection",
|
|
124
129
|
label: "Test Connection",
|
|
@@ -224,6 +229,19 @@ ALWAYS schedule follow-ups in Kanvas so the human team can see them — NEVER st
|
|
|
224
229
|
- \`kanvas_list_events\` → list scheduled events/follow-ups
|
|
225
230
|
- For structured follow-up data (tracking status, priority, custom fields), use \`kanvas_create_message\` with verb "follow_up" and a JSON payload containing { due_date, lead_id, action, status, priority }.
|
|
226
231
|
|
|
232
|
+
**Ecosystem (Companies, Branches, Roles, Users)**
|
|
233
|
+
Use when the user asks about companies, branches/locations, user management, roles, or permissions.
|
|
234
|
+
- \`kanvas_list_companies\` → list/search companies
|
|
235
|
+
- \`kanvas_list_branches\` → list/search branches (filter by companies_id for a specific company)
|
|
236
|
+
- \`kanvas_list_roles\` → list available roles (find role IDs like "super admin")
|
|
237
|
+
- \`kanvas_list_company_users\` → list users in the current company
|
|
238
|
+
- \`kanvas_add_user_to_company\` → add a user to a company with optional role
|
|
239
|
+
- \`kanvas_remove_user_from_company\` → remove a user from a company
|
|
240
|
+
- \`kanvas_add_user_to_branch\` → add a user to a branch/location
|
|
241
|
+
- \`kanvas_remove_user_from_branch\` → remove a user from a branch
|
|
242
|
+
- \`kanvas_assign_role_to_user\` → assign roles (e.g. super admin). Call \`kanvas_list_roles\` first to get role IDs.
|
|
243
|
+
- \`kanvas_remove_role_from_user\` → remove a role from a user
|
|
244
|
+
|
|
227
245
|
**Diagnostics**
|
|
228
246
|
- \`kanvas_test_connection\` → verify the API is reachable
|
|
229
247
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { toolResult } from "./helpers.js";
|
|
3
|
+
const WhereClause = Type.Optional(Type.Array(Type.Object({
|
|
4
|
+
column: Type.String(),
|
|
5
|
+
operator: Type.String({ description: 'e.g. "EQ", "LIKE"' }),
|
|
6
|
+
value: Type.Unknown(),
|
|
7
|
+
}), { description: "Filter conditions" }));
|
|
8
|
+
export function registerEcosystemTools(api, service, ensureAuth) {
|
|
9
|
+
// ── Read tools ─────────────────────────────────────────────
|
|
10
|
+
api.registerTool({
|
|
11
|
+
name: "kanvas_list_companies",
|
|
12
|
+
label: "List Companies",
|
|
13
|
+
description: "List or search companies in the Kanvas ecosystem.",
|
|
14
|
+
parameters: Type.Object({
|
|
15
|
+
first: Type.Optional(Type.Number({ description: "Max results (default 25)" })),
|
|
16
|
+
search: Type.Optional(Type.String({ description: "Search keyword" })),
|
|
17
|
+
where: WhereClause,
|
|
18
|
+
}),
|
|
19
|
+
async execute(_id, params) {
|
|
20
|
+
await ensureAuth();
|
|
21
|
+
return toolResult(await service.listCompanies(params.first, params.search, params.where));
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
api.registerTool({
|
|
25
|
+
name: "kanvas_list_branches",
|
|
26
|
+
label: "List Branches",
|
|
27
|
+
description: "List or search company branches. Filter by companies_id to get branches for a specific company.",
|
|
28
|
+
parameters: Type.Object({
|
|
29
|
+
first: Type.Optional(Type.Number({ description: "Max results (default 25)" })),
|
|
30
|
+
search: Type.Optional(Type.String({ description: "Search keyword" })),
|
|
31
|
+
where: WhereClause,
|
|
32
|
+
}),
|
|
33
|
+
async execute(_id, params) {
|
|
34
|
+
await ensureAuth();
|
|
35
|
+
return toolResult(await service.listBranches(params.first, params.search, params.where));
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
api.registerTool({
|
|
39
|
+
name: "kanvas_list_roles",
|
|
40
|
+
label: "List Roles",
|
|
41
|
+
description: "List available roles. Use this to find role IDs (e.g. super admin) before assigning roles to users.",
|
|
42
|
+
parameters: Type.Object({
|
|
43
|
+
first: Type.Optional(Type.Number({ description: "Max results (default 50)" })),
|
|
44
|
+
search: Type.Optional(Type.String({ description: "Search by role name" })),
|
|
45
|
+
}),
|
|
46
|
+
async execute(_id, params) {
|
|
47
|
+
await ensureAuth();
|
|
48
|
+
return toolResult(await service.listRoles(params.first, params.search));
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
api.registerTool({
|
|
52
|
+
name: "kanvas_list_company_users",
|
|
53
|
+
label: "List Company Users",
|
|
54
|
+
description: "List users in the current company. Supports search and filtering by user fields.",
|
|
55
|
+
parameters: Type.Object({
|
|
56
|
+
first: Type.Optional(Type.Number({ description: "Max results (default 25)" })),
|
|
57
|
+
search: Type.Optional(Type.String({ description: "Search keyword" })),
|
|
58
|
+
where: WhereClause,
|
|
59
|
+
}),
|
|
60
|
+
async execute(_id, params) {
|
|
61
|
+
await ensureAuth();
|
|
62
|
+
return toolResult(await service.listCompanyUsers(params.first, params.search, params.where));
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
// ── Write tools ────────────────────────────────────────────
|
|
66
|
+
api.registerTool({
|
|
67
|
+
name: "kanvas_add_user_to_company",
|
|
68
|
+
label: "Add User to Company",
|
|
69
|
+
description: "Add an existing user to a company, optionally with a specific role.",
|
|
70
|
+
parameters: Type.Object({
|
|
71
|
+
company_id: Type.String({ description: "Company ID" }),
|
|
72
|
+
user_id: Type.String({ description: "User ID" }),
|
|
73
|
+
role_id: Type.Optional(Type.String({ description: "Role ID (use kanvas_list_roles to find it)" })),
|
|
74
|
+
}),
|
|
75
|
+
async execute(_id, params) {
|
|
76
|
+
await ensureAuth();
|
|
77
|
+
return toolResult(await service.addUserToCompany(params.company_id, params.user_id, params.role_id));
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
api.registerTool({
|
|
81
|
+
name: "kanvas_remove_user_from_company",
|
|
82
|
+
label: "Remove User from Company",
|
|
83
|
+
description: "Remove a user from a company.",
|
|
84
|
+
parameters: Type.Object({
|
|
85
|
+
company_id: Type.String({ description: "Company ID" }),
|
|
86
|
+
user_id: Type.String({ description: "User ID" }),
|
|
87
|
+
}),
|
|
88
|
+
async execute(_id, params) {
|
|
89
|
+
await ensureAuth();
|
|
90
|
+
return toolResult(await service.removeUserFromCompany(params.company_id, params.user_id));
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
api.registerTool({
|
|
94
|
+
name: "kanvas_add_user_to_branch",
|
|
95
|
+
label: "Add User to Branch",
|
|
96
|
+
description: "Add a user to a company branch/location.",
|
|
97
|
+
parameters: Type.Object({
|
|
98
|
+
branch_id: Type.String({ description: "Branch ID" }),
|
|
99
|
+
user_id: Type.String({ description: "User ID" }),
|
|
100
|
+
}),
|
|
101
|
+
async execute(_id, params) {
|
|
102
|
+
await ensureAuth();
|
|
103
|
+
return toolResult(await service.addUserToBranch(params.branch_id, params.user_id));
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
api.registerTool({
|
|
107
|
+
name: "kanvas_remove_user_from_branch",
|
|
108
|
+
label: "Remove User from Branch",
|
|
109
|
+
description: "Remove a user from a company branch/location.",
|
|
110
|
+
parameters: Type.Object({
|
|
111
|
+
branch_id: Type.String({ description: "Branch ID" }),
|
|
112
|
+
user_id: Type.String({ description: "User ID" }),
|
|
113
|
+
}),
|
|
114
|
+
async execute(_id, params) {
|
|
115
|
+
await ensureAuth();
|
|
116
|
+
return toolResult(await service.removeUserFromBranch(params.branch_id, params.user_id));
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
api.registerTool({
|
|
120
|
+
name: "kanvas_assign_role_to_user",
|
|
121
|
+
label: "Assign Role to User",
|
|
122
|
+
description: "Assign one or more roles to a user. Requires admin privileges. Use kanvas_list_roles to find role IDs.",
|
|
123
|
+
parameters: Type.Object({
|
|
124
|
+
user_id: Type.String({ description: "User ID" }),
|
|
125
|
+
role_ids: Type.Array(Type.String(), { description: "Array of role IDs to assign" }),
|
|
126
|
+
}),
|
|
127
|
+
async execute(_id, params) {
|
|
128
|
+
await ensureAuth();
|
|
129
|
+
return toolResult(await service.assignRoleToUser(params.user_id, params.role_ids));
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
api.registerTool({
|
|
133
|
+
name: "kanvas_remove_role_from_user",
|
|
134
|
+
label: "Remove Role from User",
|
|
135
|
+
description: "Remove a role from a user. The role parameter can be the role ID or name.",
|
|
136
|
+
parameters: Type.Object({
|
|
137
|
+
user_id: Type.String({ description: "User ID" }),
|
|
138
|
+
role: Type.String({ description: "Role ID or name to remove" }),
|
|
139
|
+
}),
|
|
140
|
+
async execute(_id, params) {
|
|
141
|
+
await ensureAuth();
|
|
142
|
+
return toolResult(await service.removeRoleFromUser(params.user_id, params.role));
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
package/package.json
CHANGED