openclaw-productboard 1.0.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 +35 -0
- package/LICENSE +21 -0
- package/README.md +230 -0
- package/dist/client/api-client.d.ts +64 -0
- package/dist/client/api-client.js +379 -0
- package/dist/client/errors.d.ts +51 -0
- package/dist/client/errors.js +128 -0
- package/dist/client/types.d.ts +262 -0
- package/dist/client/types.js +6 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +62 -0
- package/dist/tools/features.d.ts +6 -0
- package/dist/tools/features.js +318 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/notes.d.ts +6 -0
- package/dist/tools/notes.js +176 -0
- package/dist/tools/products.d.ts +6 -0
- package/dist/tools/products.js +148 -0
- package/dist/tools/search.d.ts +6 -0
- package/dist/tools/search.js +116 -0
- package/dist/utils/cache.d.ts +54 -0
- package/dist/utils/cache.js +123 -0
- package/dist/utils/rate-limiter.d.ts +58 -0
- package/dist/utils/rate-limiter.js +118 -0
- package/openclaw.plugin.json +57 -0
- package/package.json +53 -0
- package/skills/productboard-feedback/SKILL.md +105 -0
- package/skills/productboard-release/SKILL.md +146 -0
- package/skills/productboard-search/SKILL.md +62 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ProductBoard Product Management Tools
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createProductTools = createProductTools;
|
|
7
|
+
function createProductTools(client) {
|
|
8
|
+
return [
|
|
9
|
+
// pb_product_list
|
|
10
|
+
{
|
|
11
|
+
name: 'pb_product_list',
|
|
12
|
+
description: 'List all products in the ProductBoard workspace. Products are top-level containers for organizing features.',
|
|
13
|
+
parameters: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
limit: {
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'Maximum number of products to return (default: 50)',
|
|
19
|
+
default: 50,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
handler: async (params) => {
|
|
24
|
+
const products = await client.listProducts({
|
|
25
|
+
limit: params.limit || 50,
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
count: products.length,
|
|
29
|
+
products: products.map((p) => ({
|
|
30
|
+
id: p.id,
|
|
31
|
+
name: p.name,
|
|
32
|
+
description: p.description?.substring(0, 200),
|
|
33
|
+
createdAt: p.createdAt,
|
|
34
|
+
url: p.links?.html,
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
// pb_product_get
|
|
40
|
+
{
|
|
41
|
+
name: 'pb_product_get',
|
|
42
|
+
description: 'Get detailed information about a specific product by ID, including its components.',
|
|
43
|
+
parameters: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
id: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'Product ID',
|
|
49
|
+
},
|
|
50
|
+
includeComponents: {
|
|
51
|
+
type: 'boolean',
|
|
52
|
+
description: 'Include the list of components under this product',
|
|
53
|
+
default: true,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: ['id'],
|
|
57
|
+
},
|
|
58
|
+
handler: async (params) => {
|
|
59
|
+
const product = await client.getProduct(params.id);
|
|
60
|
+
const result = {
|
|
61
|
+
id: product.id,
|
|
62
|
+
name: product.name,
|
|
63
|
+
description: product.description,
|
|
64
|
+
createdAt: product.createdAt,
|
|
65
|
+
updatedAt: product.updatedAt,
|
|
66
|
+
url: product.links?.html,
|
|
67
|
+
};
|
|
68
|
+
// Optionally include components
|
|
69
|
+
if (params.includeComponents !== false) {
|
|
70
|
+
const components = await client.listComponents({
|
|
71
|
+
productId: product.id,
|
|
72
|
+
limit: 100,
|
|
73
|
+
});
|
|
74
|
+
result.components = components.map((c) => ({
|
|
75
|
+
id: c.id,
|
|
76
|
+
name: c.name,
|
|
77
|
+
description: c.description?.substring(0, 200),
|
|
78
|
+
}));
|
|
79
|
+
result.componentCount = components.length;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
// pb_product_hierarchy
|
|
85
|
+
{
|
|
86
|
+
name: 'pb_product_hierarchy',
|
|
87
|
+
description: 'Get the complete product hierarchy including all products and their components. Useful for understanding the workspace structure.',
|
|
88
|
+
parameters: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {},
|
|
91
|
+
},
|
|
92
|
+
handler: async () => {
|
|
93
|
+
const hierarchy = await client.getProductHierarchy();
|
|
94
|
+
// Build tree structure
|
|
95
|
+
const productMap = new Map();
|
|
96
|
+
// Initialize products
|
|
97
|
+
for (const product of hierarchy.products) {
|
|
98
|
+
productMap.set(product.id, {
|
|
99
|
+
id: product.id,
|
|
100
|
+
name: product.name,
|
|
101
|
+
description: product.description?.substring(0, 200),
|
|
102
|
+
components: [],
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// Build component tree (components can be nested)
|
|
106
|
+
const componentMap = new Map();
|
|
107
|
+
for (const component of hierarchy.components) {
|
|
108
|
+
componentMap.set(component.id, {
|
|
109
|
+
...component,
|
|
110
|
+
subcomponents: [],
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Assign components to parents
|
|
114
|
+
for (const component of hierarchy.components) {
|
|
115
|
+
const comp = componentMap.get(component.id);
|
|
116
|
+
if (component.parent?.product?.id) {
|
|
117
|
+
// Top-level component under a product
|
|
118
|
+
const product = productMap.get(component.parent.product.id);
|
|
119
|
+
if (product) {
|
|
120
|
+
product.components.push({
|
|
121
|
+
id: comp.id,
|
|
122
|
+
name: comp.name,
|
|
123
|
+
description: comp.description?.substring(0, 200),
|
|
124
|
+
subcomponents: comp.subcomponents,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (component.parent?.component?.id) {
|
|
129
|
+
// Nested component
|
|
130
|
+
const parent = componentMap.get(component.parent.component.id);
|
|
131
|
+
if (parent) {
|
|
132
|
+
parent.subcomponents.push({
|
|
133
|
+
id: comp.id,
|
|
134
|
+
name: comp.name,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
productCount: hierarchy.products.length,
|
|
141
|
+
componentCount: hierarchy.components.length,
|
|
142
|
+
hierarchy: Array.from(productMap.values()),
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZHVjdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdG9vbHMvcHJvZHVjdHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOztHQUVHOztBQUtILGdEQW9LQztBQXBLRCxTQUFnQixrQkFBa0IsQ0FBQyxNQUEwQjtJQUMzRCxPQUFPO1FBQ0wsa0JBQWtCO1FBQ2xCO1lBQ0UsSUFBSSxFQUFFLGlCQUFpQjtZQUN2QixXQUFXLEVBQ1QsNkdBQTZHO1lBQy9HLFVBQVUsRUFBRTtnQkFDVixJQUFJLEVBQUUsUUFBUTtnQkFDZCxVQUFVLEVBQUU7b0JBQ1YsS0FBSyxFQUFFO3dCQUNMLElBQUksRUFBRSxRQUFRO3dCQUNkLFdBQVcsRUFBRSxvREFBb0Q7d0JBQ2pFLE9BQU8sRUFBRSxFQUFFO3FCQUNaO2lCQUNGO2FBQ0Y7WUFDRCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxFQUFFO2dCQUN4QixNQUFNLFFBQVEsR0FBRyxNQUFNLE1BQU0sQ0FBQyxZQUFZLENBQUM7b0JBQ3pDLEtBQUssRUFBRSxNQUFNLENBQUMsS0FBZSxJQUFJLEVBQUU7aUJBQ3BDLENBQUMsQ0FBQztnQkFDSCxPQUFPO29CQUNMLEtBQUssRUFBRSxRQUFRLENBQUMsTUFBTTtvQkFDdEIsUUFBUSxFQUFFLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7d0JBQzdCLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRTt3QkFDUixJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUk7d0JBQ1osV0FBVyxFQUFFLENBQUMsQ0FBQyxXQUFXLEVBQUUsU0FBUyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUM7d0JBQzdDLFNBQVMsRUFBRSxDQUFDLENBQUMsU0FBUzt3QkFDdEIsR0FBRyxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSTtxQkFDbkIsQ0FBQyxDQUFDO2lCQUNKLENBQUM7WUFDSixDQUFDO1NBQ0Y7UUFF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QUMsSUFBSSxDQUFDO2dDQUN4QixFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUU7Z0NBQ1gsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJOzZCQUNoQixDQUFDLENBQUM7d0JBQ0wsQ0FBQztvQkFDSCxDQUFDO2dCQUNILENBQUM7Z0JBRUQsT0FBTztvQkFDTCxZQUFZLEVBQUUsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNO29CQUN2QyxjQUFjLEVBQUUsU0FBUyxDQUFDLFVBQVUsQ0FBQyxNQUFNO29CQUMzQyxTQUFTLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLENBQUM7aUJBQzNDLENBQUM7WUFDSixDQUFDO1NBQ0Y7S0FDRixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUHJvZHVjdEJvYXJkIFByb2R1Y3QgTWFuYWdlbWVudCBUb29sc1xuICovXG5cbmltcG9ydCB7IFByb2R1Y3RCb2FyZENsaWVudCB9IGZyb20gJy4uL2NsaWVudC9hcGktY2xpZW50JztcbmltcG9ydCB7IFRvb2xEZWZpbml0aW9uIH0gZnJvbSAnLi4vY2xpZW50L3R5cGVzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVByb2R1Y3RUb29scyhjbGllbnQ6IFByb2R1Y3RCb2FyZENsaWVudCk6IFRvb2xEZWZpbml0aW9uW10ge1xuICByZXR1cm4gW1xuICAgIC8vIHBiX3Byb2R1Y3RfbGlzdFxuICAgIHtcbiAgICAgIG5hbWU6ICdwYl9wcm9kdWN0X2xpc3QnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdMaXN0IGFsbCBwcm9kdWN0cyBpbiB0aGUgUHJvZHVjdEJvYXJkIHdvcmtzcGFjZS4gUHJvZHVjdHMgYXJlIHRvcC1sZXZlbCBjb250YWluZXJzIGZvciBvcmdhbml6aW5nIGZlYXR1cmVzLicsXG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgbGltaXQ6IHtcbiAgICAgICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiBwcm9kdWN0cyB0byByZXR1cm4gKGRlZmF1bHQ6IDUwKScsXG4gICAgICAgICAgICBkZWZhdWx0OiA1MCxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChwYXJhbXMpID0+IHtcbiAgICAgICAgY29uc3QgcHJvZHVjdHMgPSBhd2FpdCBjbGllbnQubGlzdFByb2R1Y3RzKHtcbiAgICAgICAgICBsaW1pdDogcGFyYW1zLmxpbWl0IGFzIG51bWJlciB8fCA1MCxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgY291bnQ6IHByb2R1Y3RzLmxlbmd0aCxcbiAgICAgICAgICBwcm9kdWN0czogcHJvZHVjdHMubWFwKChwKSA9PiAoe1xuICAgICAgICAgICAgaWQ6IHAuaWQsXG4gICAgICAgICAgICBuYW1lOiBwLm5hbWUsXG4gICAgICAgICAgICBkZXNjcmlwdGlvbjogcC5kZXNjcmlwdGlvbj8uc3Vic3RyaW5nKDAsIDIwMCksXG4gICAgICAgICAgICBjcmVhdGVkQXQ6IHAuY3JlYXRlZEF0LFxuICAgICAgICAgICAgdXJsOiBwLmxpbmtzPy5odG1sLFxuICAgICAgICAgIH0pKSxcbiAgICAgICAgfTtcbiAgICAgIH0sXG4gICAgfSxcblxuICAgIC8vIHBiX3Byb2R1Y3RfZ2V0XG4gICAge1xuICAgICAgbmFtZTogJ3BiX3Byb2R1Y3RfZ2V0JyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnR2V0IGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IGEgc3BlY2lmaWMgcHJvZHVjdCBieSBJRCwgaW5jbHVkaW5nIGl0cyBjb21wb25lbnRzLicsXG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgaWQ6IHtcbiAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcm9kdWN0IElEJyxcbiAgICAgICAgICB9LFxuICAgICAgICAgIGluY2x1ZGVDb21wb25lbnRzOiB7XG4gICAgICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0luY2x1ZGUgdGhlIGxpc3Qgb2YgY29tcG9uZW50cyB1bmRlciB0aGlzIHByb2R1Y3QnLFxuICAgICAgICAgICAgZGVmYXVsdDogdHJ1ZSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICByZXF1aXJlZDogWydpZCddLFxuICAgICAgfSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChwYXJhbXMpID0+IHtcbiAgICAgICAgY29uc3QgcHJvZHVjdCA9IGF3YWl0IGNsaWVudC5nZXRQcm9kdWN0KHBhcmFtcy5pZCBhcyBzdHJpbmcpO1xuXG4gICAgICAgIGNvbnN0IHJlc3VsdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7XG4gICAgICAgICAgaWQ6IHByb2R1Y3QuaWQsXG4gICAgICAgICAgbmFtZTogcHJvZHVjdC5uYW1lLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBwcm9kdWN0LmRlc2NyaXB0aW9uLFxuICAgICAgICAgIGNyZWF0ZWRBdDogcHJvZHVjdC5jcmVhdGVkQXQsXG4gICAgICAgICAgdXBkYXRlZEF0OiBwcm9kdWN0LnVwZGF0ZWRBdCxcbiAgICAgICAgICB1cmw6IHByb2R1Y3QubGlua3M/Lmh0bWwsXG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gT3B0aW9uYWxseSBpbmNsdWRlIGNvbXBvbmVudHNcbiAgICAgICAgaWYgKHBhcmFtcy5pbmNsdWRlQ29tcG9uZW50cyAhPT0gZmFsc2UpIHtcbiAgICAgICAgICBjb25zdCBjb21wb25lbnRzID0gYXdhaXQgY2xpZW50Lmxpc3RDb21wb25lbnRzKHtcbiAgICAgICAgICAgIHByb2R1Y3RJZDogcHJvZHVjdC5pZCxcbiAgICAgICAgICAgIGxpbWl0OiAxMDAsXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgcmVzdWx0LmNvbXBvbmVudHMgPSBjb21wb25lbnRzLm1hcCgoYykgPT4gKHtcbiAgICAgICAgICAgIGlkOiBjLmlkLFxuICAgICAgICAgICAgbmFtZTogYy5uYW1lLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246IGMuZGVzY3JpcHRpb24/LnN1YnN0cmluZygwLCAyMDApLFxuICAgICAgICAgIH0pKTtcbiAgICAgICAgICByZXN1bHQuY29tcG9uZW50Q291bnQgPSBjb21wb25lbnRzLmxlbmd0aDtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9LFxuICAgIH0sXG5cbiAgICAvLyBwYl9wcm9kdWN0X2hpZXJhcmNoeVxuICAgIHtcbiAgICAgIG5hbWU6ICdwYl9wcm9kdWN0X2hpZXJhcmNoeScsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0dldCB0aGUgY29tcGxldGUgcHJvZHVjdCBoaWVyYXJjaHkgaW5jbHVkaW5nIGFsbCBwcm9kdWN0cyBhbmQgdGhlaXIgY29tcG9uZW50cy4gVXNlZnVsIGZvciB1bmRlcnN0YW5kaW5nIHRoZSB3b3Jrc3BhY2Ugc3RydWN0dXJlLicsXG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICBwcm9wZXJ0aWVzOiB7fSxcbiAgICAgIH0sXG4gICAgICBoYW5kbGVyOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIGNvbnN0IGhpZXJhcmNoeSA9IGF3YWl0IGNsaWVudC5nZXRQcm9kdWN0SGllcmFyY2h5KCk7XG5cbiAgICAgICAgLy8gQnVpbGQgdHJlZSBzdHJ1Y3R1cmVcbiAgICAgICAgY29uc3QgcHJvZHVjdE1hcCA9IG5ldyBNYXA8c3RyaW5nLCB7XG4gICAgICAgICAgaWQ6IHN0cmluZztcbiAgICAgICAgICBuYW1lOiBzdHJpbmc7XG4gICAgICAgICAgZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gICAgICAgICAgY29tcG9uZW50czogQXJyYXk8e1xuICAgICAgICAgICAgaWQ6IHN0cmluZztcbiAgICAgICAgICAgIG5hbWU6IHN0cmluZztcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uPzogc3RyaW5nO1xuICAgICAgICAgICAgc3ViY29tcG9uZW50czogQXJyYXk8eyBpZDogc3RyaW5nOyBuYW1lOiBzdHJpbmcgfT47XG4gICAgICAgICAgfT47XG4gICAgICAgIH0+KCk7XG5cbiAgICAgICAgLy8gSW5pdGlhbGl6ZSBwcm9kdWN0c1xuICAgICAgICBmb3IgKGNvbnN0IHByb2R1Y3Qgb2YgaGllcmFyY2h5LnByb2R1Y3RzKSB7XG4gICAgICAgICAgcHJvZHVjdE1hcC5zZXQocHJvZHVjdC5pZCwge1xuICAgICAgICAgICAgaWQ6IHByb2R1Y3QuaWQsXG4gICAgICAgICAgICBuYW1lOiBwcm9kdWN0Lm5hbWUsXG4gICAgICAgICAgICBkZXNjcmlwdGlvbjogcHJvZHVjdC5kZXNjcmlwdGlvbj8uc3Vic3RyaW5nKDAsIDIwMCksXG4gICAgICAgICAgICBjb21wb25lbnRzOiBbXSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEJ1aWxkIGNvbXBvbmVudCB0cmVlIChjb21wb25lbnRzIGNhbiBiZSBuZXN0ZWQpXG4gICAgICAgIGNvbnN0IGNvbXBvbmVudE1hcCA9IG5ldyBNYXA8c3RyaW5nLCB0eXBlb2YgaGllcmFyY2h5LmNvbXBvbmVudHNbMF0gJiB7IHN1YmNvbXBvbmVudHM6IEFycmF5PHsgaWQ6IHN0cmluZzsgbmFtZTogc3RyaW5nIH0+IH0+KCk7XG5cbiAgICAgICAgZm9yIChjb25zdCBjb21wb25lbnQgb2YgaGllcmFyY2h5LmNvbXBvbmVudHMpIHtcbiAgICAgICAgICBjb21wb25lbnRNYXAuc2V0KGNvbXBvbmVudC5pZCwge1xuICAgICAgICAgICAgLi4uY29tcG9uZW50LFxuICAgICAgICAgICAgc3ViY29tcG9uZW50czogW10sXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBc3NpZ24gY29tcG9uZW50cyB0byBwYXJlbnRzXG4gICAgICAgIGZvciAoY29uc3QgY29tcG9uZW50IG9mIGhpZXJhcmNoeS5jb21wb25lbnRzKSB7XG4gICAgICAgICAgY29uc3QgY29tcCA9IGNvbXBvbmVudE1hcC5nZXQoY29tcG9uZW50LmlkKSE7XG5cbiAgICAgICAgICBpZiAoY29tcG9uZW50LnBhcmVudD8ucHJvZHVjdD8uaWQpIHtcbiAgICAgICAgICAgIC8vIFRvcC1sZXZlbCBjb21wb25lbnQgdW5kZXIgYSBwcm9kdWN0XG4gICAgICAgICAgICBjb25zdCBwcm9kdWN0ID0gcHJvZHVjdE1hcC5nZXQoY29tcG9uZW50LnBhcmVudC5wcm9kdWN0LmlkKTtcbiAgICAgICAgICAgIGlmIChwcm9kdWN0KSB7XG4gICAgICAgICAgICAgIHByb2R1Y3QuY29tcG9uZW50cy5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogY29tcC5pZCxcbiAgICAgICAgICAgICAgICBuYW1lOiBjb21wLm5hbWUsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246IGNvbXAuZGVzY3JpcHRpb24/LnN1YnN0cmluZygwLCAyMDApLFxuICAgICAgICAgICAgICAgIHN1YmNvbXBvbmVudHM6IGNvbXAuc3ViY29tcG9uZW50cyxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChjb21wb25lbnQucGFyZW50Py5jb21wb25lbnQ/LmlkKSB7XG4gICAgICAgICAgICAvLyBOZXN0ZWQgY29tcG9uZW50XG4gICAgICAgICAgICBjb25zdCBwYXJlbnQgPSBjb21wb25lbnRNYXAuZ2V0KGNvbXBvbmVudC5wYXJlbnQuY29tcG9uZW50LmlkKTtcbiAgICAgICAgICAgIGlmIChwYXJlbnQpIHtcbiAgICAgICAgICAgICAgcGFyZW50LnN1YmNvbXBvbmVudHMucHVzaCh7XG4gICAgICAgICAgICAgICAgaWQ6IGNvbXAuaWQsXG4gICAgICAgICAgICAgICAgbmFtZTogY29tcC5uYW1lLFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHByb2R1Y3RDb3VudDogaGllcmFyY2h5LnByb2R1Y3RzLmxlbmd0aCxcbiAgICAgICAgICBjb21wb25lbnRDb3VudDogaGllcmFyY2h5LmNvbXBvbmVudHMubGVuZ3RoLFxuICAgICAgICAgIGhpZXJhcmNoeTogQXJyYXkuZnJvbShwcm9kdWN0TWFwLnZhbHVlcygpKSxcbiAgICAgICAgfTtcbiAgICAgIH0sXG4gICAgfSxcbiAgXTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ProductBoard Search and User Tools
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createSearchTools = createSearchTools;
|
|
7
|
+
function createSearchTools(client) {
|
|
8
|
+
return [
|
|
9
|
+
// pb_search
|
|
10
|
+
{
|
|
11
|
+
name: 'pb_search',
|
|
12
|
+
description: 'Global search across ProductBoard. Searches features, products, components, and notes by name, title, or content.',
|
|
13
|
+
parameters: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
query: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Search query text',
|
|
19
|
+
},
|
|
20
|
+
type: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Limit search to specific type',
|
|
23
|
+
enum: ['feature', 'product', 'component', 'note'],
|
|
24
|
+
},
|
|
25
|
+
limit: {
|
|
26
|
+
type: 'number',
|
|
27
|
+
description: 'Maximum results to return (default: 25)',
|
|
28
|
+
default: 25,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: ['query'],
|
|
32
|
+
},
|
|
33
|
+
handler: async (params) => {
|
|
34
|
+
const searchParams = {
|
|
35
|
+
query: params.query,
|
|
36
|
+
type: params.type,
|
|
37
|
+
limit: params.limit || 25,
|
|
38
|
+
};
|
|
39
|
+
const results = await client.search(searchParams);
|
|
40
|
+
// Group results by type
|
|
41
|
+
const grouped = {
|
|
42
|
+
features: [],
|
|
43
|
+
products: [],
|
|
44
|
+
components: [],
|
|
45
|
+
notes: [],
|
|
46
|
+
};
|
|
47
|
+
for (const result of results) {
|
|
48
|
+
const key = result.type + 's';
|
|
49
|
+
if (grouped[key]) {
|
|
50
|
+
grouped[key].push({
|
|
51
|
+
id: result.id,
|
|
52
|
+
name: result.name || result.title,
|
|
53
|
+
description: result.description || result.content,
|
|
54
|
+
url: result.links?.html,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
totalCount: results.length,
|
|
60
|
+
query: params.query,
|
|
61
|
+
results: grouped,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// pb_user_current
|
|
66
|
+
{
|
|
67
|
+
name: 'pb_user_current',
|
|
68
|
+
description: 'Get information about the currently authenticated user, including workspace details.',
|
|
69
|
+
parameters: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {},
|
|
72
|
+
},
|
|
73
|
+
handler: async () => {
|
|
74
|
+
const user = await client.getCurrentUser();
|
|
75
|
+
return {
|
|
76
|
+
id: user.id,
|
|
77
|
+
email: user.email,
|
|
78
|
+
name: user.name,
|
|
79
|
+
role: user.role,
|
|
80
|
+
workspaceId: user.workspaceId,
|
|
81
|
+
workspaceName: user.workspaceName,
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
// pb_user_list
|
|
86
|
+
{
|
|
87
|
+
name: 'pb_user_list',
|
|
88
|
+
description: 'List all users in the ProductBoard workspace. Useful for finding user IDs for assigning features.',
|
|
89
|
+
parameters: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
limit: {
|
|
93
|
+
type: 'number',
|
|
94
|
+
description: 'Maximum number of users to return (default: 100)',
|
|
95
|
+
default: 100,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
handler: async (params) => {
|
|
100
|
+
const users = await client.listUsers({
|
|
101
|
+
limit: params.limit || 100,
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
count: users.length,
|
|
105
|
+
users: users.map((u) => ({
|
|
106
|
+
id: u.id,
|
|
107
|
+
email: u.email,
|
|
108
|
+
name: u.name,
|
|
109
|
+
role: u.role,
|
|
110
|
+
})),
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VhcmNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rvb2xzL3NlYXJjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7O0dBRUc7O0FBS0gsOENBcUhDO0FBckhELFNBQWdCLGlCQUFpQixDQUFDLE1BQTBCO0lBQzFELE9BQU87UUFDTCxZQUFZO1FBQ1o7WUFDRSxJQUFJLEVBQUUsV0FBVztZQUNqQixXQUFXLEVBQ1QsbUhBQW1IO1lBQ3JILFVBQVUsRUFBRTtnQkFDVixJQUFJLEVBQUUsUUFBUTtnQkFDZCxVQUFVLEVBQUU7b0JBQ1YsS0FBSyxFQUFFO3dCQUNMLElBQUksRUFBRSxRQUFRO3dCQUNkLFdBQVcsRUFBRSxtQkFBbUI7cUJBQ2pDO29CQUNELElBQUksRUFBRTt3QkFDSixJQUFJLEVBQUUsUUFBUTt3QkFDZCxXQUFXLEVBQUUsK0JBQStCO3dCQUM1QyxJQUFJLEVBQUUsQ0FBQyxTQUFTLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUM7cUJBQ2xEO29CQUNELEtBQUssRUFBRTt3QkFDTCxJQUFJLEVBQUUsUUFBUTt3QkFDZCxXQUFXLEVBQUUseUNBQXlDO3dCQUN0RCxPQUFPLEVBQUUsRUFBRTtxQkFDWjtpQkFDRjtnQkFDRCxRQUFRLEVBQUUsQ0FBQyxPQUFPLENBQUM7YUFDcEI7WUFDRCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxFQUFFO2dCQUN4QixNQUFNLFlBQVksR0FBaUI7b0JBQ2pDLEtBQUssRUFBRSxNQUFNLENBQUMsS0FBZTtvQkFDN0IsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFnRTtvQkFDN0UsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFlLElBQUksRUFBRTtpQkFDcEMsQ0FBQztnQkFFRixNQUFNLE9BQU8sR0FBRyxNQUFNLE1BQU0sQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBRWxELHdCQUF3QjtnQkFDeEIsTUFBTSxPQUFPLEdBQThCO29CQUN6QyxRQUFRLEVBQUUsRUFBRTtvQkFDWixRQUFRLEVBQUUsRUFBRTtvQkFDWixVQUFVLEVBQUUsRUFBRTtvQkFDZCxLQUFLLEVBQUUsRUFBRTtpQkFDVixDQUFDO2dCQUVGLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUM7b0JBQzdCLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDO29CQUM5QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO3dCQUNqQixPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDOzRCQUNoQixFQUFFLEVBQUUsTUFBTSxDQUFDLEVBQUU7NEJBQ2IsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLElBQUksTUFBTSxDQUFDLEtBQUs7NEJBQ2pDLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVyxJQUFJLE1BQU0sQ0FBQyxPQUFPOzRCQUNqRCxHQUFHLEVBQUUsTUFBTSxDQUFDLEtBQUssRUFBRSxJQUFJO3lCQUN4QixDQUFDLENBQUM7b0JBQ0wsQ0FBQztnQkFDSCxDQUFDO2dCQUVELE9BQU87b0JBQ0wsVUFBVSxFQUFFLE9BQU8sQ0FBQyxNQUFNO29CQUMxQixLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQUs7b0JBQ25CLE9BQU8sRUFBRSxPQUFPO2lCQUNqQixDQUFDO1lBQ0osQ0FBQztTQUNGO1FBRUQsa0JBQWtCO1FBQ2xCO1lBQ0UsSUFBSSxFQUFFLGlCQUFpQjtZQUN2QixXQUFXLEVBQ1Qsc0ZBQXNGO1lBQ3hGLFVBQVUsRUFBRTtnQkFDVixJQUFJLEVBQUUsUUFBUTtnQkFDZCxVQUFVLEVBQUUsRUFBRTthQUNmO1lBQ0QsT0FBTyxFQUFFLEtBQUssSUFBSSxFQUFFO2dCQUNsQixNQUFNLElBQUksR0FBRyxNQUFNLE1BQU0sQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDM0MsT0FBTztvQkFDTCxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUU7b0JBQ1gsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO29CQUNqQixJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUk7b0JBQ2YsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO29CQUNmLFdBQVcsRUFBRSxJQUFJLENBQUMsV0FBVztvQkFDN0IsYUFBYSxFQUFFLElBQUksQ0FBQyxhQUFhO2lCQUNsQyxDQUFDO1lBQ0osQ0FBQztTQUNGO1FBRUQsZUFBZTtRQUNmO1lBQ0UsSUFBSSxFQUFFLGNBQWM7WUFDcEIsV0FBVyxFQUNULG1HQUFtRztZQUNyRyxVQUFVLEVBQUU7Z0JBQ1YsSUFBSSxFQUFFLFFBQVE7Z0JBQ2QsVUFBVSxFQUFFO29CQUNWLEtBQUssRUFBRTt3QkFDTCxJQUFJLEVBQUUsUUFBUTt3QkFDZCxXQUFXLEVBQUUsa0RBQWtEO3dCQUMvRCxPQUFPLEVBQUUsR0FBRztxQkFDYjtpQkFDRjthQUNGO1lBQ0QsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsRUFBRTtnQkFDeEIsTUFBTSxLQUFLLEdBQUcsTUFBTSxNQUFNLENBQUMsU0FBUyxDQUFDO29CQUNuQyxLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQWUsSUFBSSxHQUFHO2lCQUNyQyxDQUFDLENBQUM7Z0JBQ0gsT0FBTztvQkFDTCxLQUFLLEVBQUUsS0FBSyxDQUFDLE1BQU07b0JBQ25CLEtBQUssRUFBRSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO3dCQUN2QixFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUU7d0JBQ1IsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLO3dCQUNkLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSTt3QkFDWixJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUk7cUJBQ2IsQ0FBQyxDQUFDO2lCQUNKLENBQUM7WUFDSixDQUFDO1NBQ0Y7S0FDRixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUHJvZHVjdEJvYXJkIFNlYXJjaCBhbmQgVXNlciBUb29sc1xuICovXG5cbmltcG9ydCB7IFByb2R1Y3RCb2FyZENsaWVudCB9IGZyb20gJy4uL2NsaWVudC9hcGktY2xpZW50JztcbmltcG9ydCB7IFRvb2xEZWZpbml0aW9uLCBTZWFyY2hQYXJhbXMgfSBmcm9tICcuLi9jbGllbnQvdHlwZXMnO1xuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU2VhcmNoVG9vbHMoY2xpZW50OiBQcm9kdWN0Qm9hcmRDbGllbnQpOiBUb29sRGVmaW5pdGlvbltdIHtcbiAgcmV0dXJuIFtcbiAgICAvLyBwYl9zZWFyY2hcbiAgICB7XG4gICAgICBuYW1lOiAncGJfc2VhcmNoJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnR2xvYmFsIHNlYXJjaCBhY3Jvc3MgUHJvZHVjdEJvYXJkLiBTZWFyY2hlcyBmZWF0dXJlcywgcHJvZHVjdHMsIGNvbXBvbmVudHMsIGFuZCBub3RlcyBieSBuYW1lLCB0aXRsZSwgb3IgY29udGVudC4nLFxuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgIHF1ZXJ5OiB7XG4gICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU2VhcmNoIHF1ZXJ5IHRleHQnLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgdHlwZToge1xuICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0xpbWl0IHNlYXJjaCB0byBzcGVjaWZpYyB0eXBlJyxcbiAgICAgICAgICAgIGVudW06IFsnZmVhdHVyZScsICdwcm9kdWN0JywgJ2NvbXBvbmVudCcsICdub3RlJ10sXG4gICAgICAgICAgfSxcbiAgICAgICAgICBsaW1pdDoge1xuICAgICAgICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICAgICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVzdWx0cyB0byByZXR1cm4gKGRlZmF1bHQ6IDI1KScsXG4gICAgICAgICAgICBkZWZhdWx0OiAyNSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICByZXF1aXJlZDogWydxdWVyeSddLFxuICAgICAgfSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChwYXJhbXMpID0+IHtcbiAgICAgICAgY29uc3Qgc2VhcmNoUGFyYW1zOiBTZWFyY2hQYXJhbXMgPSB7XG4gICAgICAgICAgcXVlcnk6IHBhcmFtcy5xdWVyeSBhcyBzdHJpbmcsXG4gICAgICAgICAgdHlwZTogcGFyYW1zLnR5cGUgYXMgJ2ZlYXR1cmUnIHwgJ25vdGUnIHwgJ3Byb2R1Y3QnIHwgJ2NvbXBvbmVudCcgfCB1bmRlZmluZWQsXG4gICAgICAgICAgbGltaXQ6IHBhcmFtcy5saW1pdCBhcyBudW1iZXIgfHwgMjUsXG4gICAgICAgIH07XG5cbiAgICAgICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IGNsaWVudC5zZWFyY2goc2VhcmNoUGFyYW1zKTtcblxuICAgICAgICAvLyBHcm91cCByZXN1bHRzIGJ5IHR5cGVcbiAgICAgICAgY29uc3QgZ3JvdXBlZDogUmVjb3JkPHN0cmluZywgdW5rbm93bltdPiA9IHtcbiAgICAgICAgICBmZWF0dXJlczogW10sXG4gICAgICAgICAgcHJvZHVjdHM6IFtdLFxuICAgICAgICAgIGNvbXBvbmVudHM6IFtdLFxuICAgICAgICAgIG5vdGVzOiBbXSxcbiAgICAgICAgfTtcblxuICAgICAgICBmb3IgKGNvbnN0IHJlc3VsdCBvZiByZXN1bHRzKSB7XG4gICAgICAgICAgY29uc3Qga2V5ID0gcmVzdWx0LnR5cGUgKyAncyc7XG4gICAgICAgICAgaWYgKGdyb3VwZWRba2V5XSkge1xuICAgICAgICAgICAgZ3JvdXBlZFtrZXldLnB1c2goe1xuICAgICAgICAgICAgICBpZDogcmVzdWx0LmlkLFxuICAgICAgICAgICAgICBuYW1lOiByZXN1bHQubmFtZSB8fCByZXN1bHQudGl0bGUsXG4gICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiByZXN1bHQuZGVzY3JpcHRpb24gfHwgcmVzdWx0LmNvbnRlbnQsXG4gICAgICAgICAgICAgIHVybDogcmVzdWx0LmxpbmtzPy5odG1sLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0b3RhbENvdW50OiByZXN1bHRzLmxlbmd0aCxcbiAgICAgICAgICBxdWVyeTogcGFyYW1zLnF1ZXJ5LFxuICAgICAgICAgIHJlc3VsdHM6IGdyb3VwZWQsXG4gICAgICAgIH07XG4gICAgICB9LFxuICAgIH0sXG5cbiAgICAvLyBwYl91c2VyX2N1cnJlbnRcbiAgICB7XG4gICAgICBuYW1lOiAncGJfdXNlcl9jdXJyZW50JyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnR2V0IGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50bHkgYXV0aGVudGljYXRlZCB1c2VyLCBpbmNsdWRpbmcgd29ya3NwYWNlIGRldGFpbHMuJyxcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgIHByb3BlcnRpZXM6IHt9LFxuICAgICAgfSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jICgpID0+IHtcbiAgICAgICAgY29uc3QgdXNlciA9IGF3YWl0IGNsaWVudC5nZXRDdXJyZW50VXNlcigpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGlkOiB1c2VyLmlkLFxuICAgICAgICAgIGVtYWlsOiB1c2VyLmVtYWlsLFxuICAgICAgICAgIG5hbWU6IHVzZXIubmFtZSxcbiAgICAgICAgICByb2xlOiB1c2VyLnJvbGUsXG4gICAgICAgICAgd29ya3NwYWNlSWQ6IHVzZXIud29ya3NwYWNlSWQsXG4gICAgICAgICAgd29ya3NwYWNlTmFtZTogdXNlci53b3Jrc3BhY2VOYW1lLFxuICAgICAgICB9O1xuICAgICAgfSxcbiAgICB9LFxuXG4gICAgLy8gcGJfdXNlcl9saXN0XG4gICAge1xuICAgICAgbmFtZTogJ3BiX3VzZXJfbGlzdCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0xpc3QgYWxsIHVzZXJzIGluIHRoZSBQcm9kdWN0Qm9hcmQgd29ya3NwYWNlLiBVc2VmdWwgZm9yIGZpbmRpbmcgdXNlciBJRHMgZm9yIGFzc2lnbmluZyBmZWF0dXJlcy4nLFxuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgIGxpbWl0OiB7XG4gICAgICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgdXNlcnMgdG8gcmV0dXJuIChkZWZhdWx0OiAxMDApJyxcbiAgICAgICAgICAgIGRlZmF1bHQ6IDEwMCxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChwYXJhbXMpID0+IHtcbiAgICAgICAgY29uc3QgdXNlcnMgPSBhd2FpdCBjbGllbnQubGlzdFVzZXJzKHtcbiAgICAgICAgICBsaW1pdDogcGFyYW1zLmxpbWl0IGFzIG51bWJlciB8fCAxMDAsXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGNvdW50OiB1c2Vycy5sZW5ndGgsXG4gICAgICAgICAgdXNlcnM6IHVzZXJzLm1hcCgodSkgPT4gKHtcbiAgICAgICAgICAgIGlkOiB1LmlkLFxuICAgICAgICAgICAgZW1haWw6IHUuZW1haWwsXG4gICAgICAgICAgICBuYW1lOiB1Lm5hbWUsXG4gICAgICAgICAgICByb2xlOiB1LnJvbGUsXG4gICAgICAgICAgfSkpLFxuICAgICAgICB9O1xuICAgICAgfSxcbiAgICB9LFxuICBdO1xufVxuIl19
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LRU Cache for ProductBoard API responses
|
|
3
|
+
*/
|
|
4
|
+
export interface CacheOptions {
|
|
5
|
+
/** Time-to-live in milliseconds */
|
|
6
|
+
ttl: number;
|
|
7
|
+
/** Maximum number of items to cache */
|
|
8
|
+
max: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class ApiCache {
|
|
11
|
+
private cache;
|
|
12
|
+
constructor(options?: Partial<CacheOptions>);
|
|
13
|
+
/**
|
|
14
|
+
* Generate a cache key from tool name and parameters
|
|
15
|
+
*/
|
|
16
|
+
static generateKey(tool: string, params: Record<string, unknown>): string;
|
|
17
|
+
/**
|
|
18
|
+
* Get a value from the cache
|
|
19
|
+
*/
|
|
20
|
+
get<T>(key: string): T | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Set a value in the cache
|
|
23
|
+
*/
|
|
24
|
+
set<T>(key: string, value: T, ttl?: number): void;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a key exists in the cache
|
|
27
|
+
*/
|
|
28
|
+
has(key: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Delete a value from the cache
|
|
31
|
+
*/
|
|
32
|
+
delete(key: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Clear all values from the cache
|
|
35
|
+
*/
|
|
36
|
+
clear(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Invalidate cache entries matching a pattern
|
|
39
|
+
*/
|
|
40
|
+
invalidatePattern(pattern: string): number;
|
|
41
|
+
/**
|
|
42
|
+
* Get cache statistics
|
|
43
|
+
*/
|
|
44
|
+
stats(): {
|
|
45
|
+
size: number;
|
|
46
|
+
max: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Wrap an async function with caching
|
|
50
|
+
*/
|
|
51
|
+
wrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T>;
|
|
52
|
+
}
|
|
53
|
+
export declare function getCache(options?: Partial<CacheOptions>): ApiCache;
|
|
54
|
+
export declare function resetCache(): void;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LRU Cache for ProductBoard API responses
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ApiCache = void 0;
|
|
7
|
+
exports.getCache = getCache;
|
|
8
|
+
exports.resetCache = resetCache;
|
|
9
|
+
const lru_cache_1 = require("lru-cache");
|
|
10
|
+
const DEFAULT_OPTIONS = {
|
|
11
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
12
|
+
max: 500,
|
|
13
|
+
};
|
|
14
|
+
class ApiCache {
|
|
15
|
+
cache;
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
18
|
+
this.cache = new lru_cache_1.LRUCache({
|
|
19
|
+
max: opts.max,
|
|
20
|
+
ttl: opts.ttl,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generate a cache key from tool name and parameters
|
|
25
|
+
*/
|
|
26
|
+
static generateKey(tool, params) {
|
|
27
|
+
const sortedParams = Object.keys(params)
|
|
28
|
+
.sort()
|
|
29
|
+
.reduce((acc, key) => {
|
|
30
|
+
const value = params[key];
|
|
31
|
+
if (value !== undefined && value !== null) {
|
|
32
|
+
acc[key] = value;
|
|
33
|
+
}
|
|
34
|
+
return acc;
|
|
35
|
+
}, {});
|
|
36
|
+
return `${tool}:${JSON.stringify(sortedParams)}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get a value from the cache
|
|
40
|
+
*/
|
|
41
|
+
get(key) {
|
|
42
|
+
return this.cache.get(key);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Set a value in the cache
|
|
46
|
+
*/
|
|
47
|
+
set(key, value, ttl) {
|
|
48
|
+
if (ttl !== undefined) {
|
|
49
|
+
this.cache.set(key, value, { ttl });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.cache.set(key, value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if a key exists in the cache
|
|
57
|
+
*/
|
|
58
|
+
has(key) {
|
|
59
|
+
return this.cache.has(key);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Delete a value from the cache
|
|
63
|
+
*/
|
|
64
|
+
delete(key) {
|
|
65
|
+
return this.cache.delete(key);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Clear all values from the cache
|
|
69
|
+
*/
|
|
70
|
+
clear() {
|
|
71
|
+
this.cache.clear();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Invalidate cache entries matching a pattern
|
|
75
|
+
*/
|
|
76
|
+
invalidatePattern(pattern) {
|
|
77
|
+
let count = 0;
|
|
78
|
+
for (const key of this.cache.keys()) {
|
|
79
|
+
if (key.startsWith(pattern)) {
|
|
80
|
+
this.cache.delete(key);
|
|
81
|
+
count++;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return count;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get cache statistics
|
|
88
|
+
*/
|
|
89
|
+
stats() {
|
|
90
|
+
return {
|
|
91
|
+
size: this.cache.size,
|
|
92
|
+
max: this.cache.max,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Wrap an async function with caching
|
|
97
|
+
*/
|
|
98
|
+
async wrap(key, fn, ttl) {
|
|
99
|
+
const cached = this.get(key);
|
|
100
|
+
if (cached !== undefined) {
|
|
101
|
+
return cached;
|
|
102
|
+
}
|
|
103
|
+
const result = await fn();
|
|
104
|
+
this.set(key, result, ttl);
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.ApiCache = ApiCache;
|
|
109
|
+
// Singleton instance for the plugin
|
|
110
|
+
let cacheInstance = null;
|
|
111
|
+
function getCache(options) {
|
|
112
|
+
if (!cacheInstance) {
|
|
113
|
+
cacheInstance = new ApiCache(options);
|
|
114
|
+
}
|
|
115
|
+
return cacheInstance;
|
|
116
|
+
}
|
|
117
|
+
function resetCache() {
|
|
118
|
+
if (cacheInstance) {
|
|
119
|
+
cacheInstance.clear();
|
|
120
|
+
}
|
|
121
|
+
cacheInstance = null;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvY2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOztHQUVHOzs7QUFvSUgsNEJBS0M7QUFFRCxnQ0FLQztBQTlJRCx5Q0FBcUM7QUFTckMsTUFBTSxlQUFlLEdBQWlCO0lBQ3BDLEdBQUcsRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxZQUFZO0lBQ2hDLEdBQUcsRUFBRSxHQUFHO0NBQ1QsQ0FBQztBQUtGLE1BQWEsUUFBUTtJQUNYLEtBQUssQ0FBK0I7SUFFNUMsWUFBWSxVQUFpQyxFQUFFO1FBQzdDLE1BQU0sSUFBSSxHQUFHLEVBQUUsR0FBRyxlQUFlLEVBQUUsR0FBRyxPQUFPLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksb0JBQVEsQ0FBcUI7WUFDNUMsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHO1lBQ2IsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHO1NBQ2QsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFZLEVBQUUsTUFBK0I7UUFDOUQsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7YUFDckMsSUFBSSxFQUFFO2FBQ04sTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFO1lBQ25CLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMxQixJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxLQUFLLElBQUksRUFBRSxDQUFDO2dCQUMxQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDO1lBQ25CLENBQUM7WUFDRCxPQUFPLEdBQUcsQ0FBQztRQUNiLENBQUMsRUFBRSxFQUE2QixDQUFDLENBQUM7UUFFcEMsT0FBTyxHQUFHLElBQUksSUFB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KiBMUlUgQ2FjaGUgZm9yIFByb2R1Y3RCb2FyZCBBUEkgcmVzcG9uc2VzXG4gKi9cblxuaW1wb3J0IHsgTFJVQ2FjaGUgfSBmcm9tICdscnUtY2FjaGUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIENhY2hlT3B0aW9ucyB7XG4gIC8qKiBUaW1lLXRvLWxpdmUgaW4gbWlsbGlzZWNvbmRzICovXG4gIHR0bDogbnVtYmVyO1xuICAvKiogTWF4aW11bSBudW1iZXIgb2YgaXRlbXMgdG8gY2FjaGUgKi9cbiAgbWF4OiBudW1iZXI7XG59XG5cbmNvbnN0IERFRkFVTFRfT1BUSU9OUzogQ2FjaGVPcHRpb25zID0ge1xuICB0dGw6IDUgKiA2MCAqIDEwMDAsIC8vIDUgbWludXRlc1xuICBtYXg6IDUwMCxcbn07XG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG50eXBlIENhY2hlVmFsdWUgPSBhbnk7XG5cbmV4cG9ydCBjbGFzcyBBcGlDYWNoZSB7XG4gIHByaXZhdGUgY2FjaGU6IExSVUNhY2hlPHN0cmluZywgQ2FjaGVWYWx1ZT47XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogUGFydGlhbDxDYWNoZU9wdGlvbnM+ID0ge30pIHtcbiAgICBjb25zdCBvcHRzID0geyAuLi5ERUZBVUxUX09QVElPTlMsIC4uLm9wdGlvbnMgfTtcbiAgICB0aGlzLmNhY2hlID0gbmV3IExSVUNhY2hlPHN0cmluZywgQ2FjaGVWYWx1ZT4oe1xuICAgICAgbWF4OiBvcHRzLm1heCxcbiAgICAgIHR0bDogb3B0cy50dGwsXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgYSBjYWNoZSBrZXkgZnJvbSB0b29sIG5hbWUgYW5kIHBhcmFtZXRlcnNcbiAgICovXG4gIHN0YXRpYyBnZW5lcmF0ZUtleSh0b29sOiBzdHJpbmcsIHBhcmFtczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBzdHJpbmcge1xuICAgIGNvbnN0IHNvcnRlZFBhcmFtcyA9IE9iamVjdC5rZXlzKHBhcmFtcylcbiAgICAgIC5zb3J0KClcbiAgICAgIC5yZWR1Y2UoKGFjYywga2V5KSA9PiB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gcGFyYW1zW2tleV07XG4gICAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkICYmIHZhbHVlICE9PSBudWxsKSB7XG4gICAgICAgICAgYWNjW2tleV0gPSB2YWx1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSwge30gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pO1xuXG4gICAgcmV0dXJuIGAke3Rvb2x9OiR7SlNPTi5zdHJpbmdpZnkoc29ydGVkUGFyYW1zKX1gO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIHZhbHVlIGZyb20gdGhlIGNhY2hlXG4gICAqL1xuICBnZXQ8VD4oa2V5OiBzdHJpbmcpOiBUIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5jYWNoZS5nZXQoa2V5KSBhcyBUIHwgdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBhIHZhbHVlIGluIHRoZSBjYWNoZVxuICAgKi9cbiAgc2V0PFQ+KGtleTogc3RyaW5nLCB2YWx1ZTogVCwgdHRsPzogbnVtYmVyKTogdm9pZCB7XG4gICAgaWYgKHR0bCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aGlzLmNhY2hlLnNldChrZXksIHZhbHVlLCB7IHR0bCB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5jYWNoZS5zZXQoa2V5LCB2YWx1ZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGEga2V5IGV4aXN0cyBpbiB0aGUgY2FjaGVcbiAgICovXG4gIGhhcyhrZXk6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNhY2hlLmhhcyhrZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIERlbGV0ZSBhIHZhbHVlIGZyb20gdGhlIGNhY2hlXG4gICAqL1xuICBkZWxldGUoa2V5OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jYWNoZS5kZWxldGUoa2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciBhbGwgdmFsdWVzIGZyb20gdGhlIGNhY2hlXG4gICAqL1xuICBjbGVhcigpOiB2b2lkIHtcbiAgICB0aGlzLmNhY2hlLmNsZWFyKCk7XG4gIH1cblxuICAvKipcbiAgICogSW52YWxpZGF0ZSBjYWNoZSBlbnRyaWVzIG1hdGNoaW5nIGEgcGF0dGVyblxuICAgKi9cbiAgaW52YWxpZGF0ZVBhdHRlcm4ocGF0dGVybjogc3RyaW5nKTogbnVtYmVyIHtcbiAgICBsZXQgY291bnQgPSAwO1xuICAgIGZvciAoY29uc3Qga2V5IG9mIHRoaXMuY2FjaGUua2V5cygpKSB7XG4gICAgICBpZiAoa2V5LnN0YXJ0c1dpdGgocGF0dGVybikpIHtcbiAgICAgICAgdGhpcy5jYWNoZS5kZWxldGUoa2V5KTtcbiAgICAgICAgY291bnQrKztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGNvdW50O1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBjYWNoZSBzdGF0aXN0aWNzXG4gICAqL1xuICBzdGF0cygpOiB7IHNpemU6IG51bWJlcjsgbWF4OiBudW1iZXIgfSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNpemU6IHRoaXMuY2FjaGUuc2l6ZSxcbiAgICAgIG1heDogdGhpcy5jYWNoZS5tYXgsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXcmFwIGFuIGFzeW5jIGZ1bmN0aW9uIHdpdGggY2FjaGluZ1xuICAgKi9cbiAgYXN5bmMgd3JhcDxUPihcbiAgICBrZXk6IHN0cmluZyxcbiAgICBmbjogKCkgPT4gUHJvbWlzZTxUPixcbiAgICB0dGw/OiBudW1iZXJcbiAgKTogUHJvbWlzZTxUPiB7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5nZXQ8VD4oa2V5KTtcbiAgICBpZiAoY2FjaGVkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBjYWNoZWQ7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZm4oKTtcbiAgICB0aGlzLnNldChrZXksIHJlc3VsdCwgdHRsKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG59XG5cbi8vIFNpbmdsZXRvbiBpbnN0YW5jZSBmb3IgdGhlIHBsdWdpblxubGV0IGNhY2hlSW5zdGFuY2U6IEFwaUNhY2hlIHwgbnVsbCA9IG51bGw7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZShvcHRpb25zPzogUGFydGlhbDxDYWNoZU9wdGlvbnM+KTogQXBpQ2FjaGUge1xuICBpZiAoIWNhY2hlSW5zdGFuY2UpIHtcbiAgICBjYWNoZUluc3RhbmNlID0gbmV3IEFwaUNhY2hlKG9wdGlvbnMpO1xuICB9XG4gIHJldHVybiBjYWNoZUluc3RhbmNlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVzZXRDYWNoZSgpOiB2b2lkIHtcbiAgaWYgKGNhY2hlSW5zdGFuY2UpIHtcbiAgICBjYWNoZUluc3RhbmNlLmNsZWFyKCk7XG4gIH1cbiAgY2FjaGVJbnN0YW5jZSA9IG51bGw7XG59XG4iXX0=
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Bucket Rate Limiter for ProductBoard API
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimiterOptions {
|
|
5
|
+
/** Maximum tokens in the bucket */
|
|
6
|
+
maxTokens: number;
|
|
7
|
+
/** Tokens added per interval */
|
|
8
|
+
refillRate: number;
|
|
9
|
+
/** Refill interval in milliseconds */
|
|
10
|
+
refillInterval: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class RateLimiter {
|
|
13
|
+
private tokens;
|
|
14
|
+
private lastRefill;
|
|
15
|
+
private readonly options;
|
|
16
|
+
constructor(options?: Partial<RateLimiterOptions>);
|
|
17
|
+
/**
|
|
18
|
+
* Refill tokens based on elapsed time
|
|
19
|
+
*/
|
|
20
|
+
private refill;
|
|
21
|
+
/**
|
|
22
|
+
* Try to acquire a token
|
|
23
|
+
* @returns true if token was acquired, false if rate limited
|
|
24
|
+
*/
|
|
25
|
+
tryAcquire(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Acquire a token, waiting if necessary
|
|
28
|
+
* @returns Promise that resolves when a token is available
|
|
29
|
+
*/
|
|
30
|
+
acquire(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Get the time in milliseconds until a token is available
|
|
33
|
+
*/
|
|
34
|
+
getWaitTime(): number;
|
|
35
|
+
/**
|
|
36
|
+
* Get current token count
|
|
37
|
+
*/
|
|
38
|
+
getTokens(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Reset the rate limiter to full capacity
|
|
41
|
+
*/
|
|
42
|
+
reset(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Check if rate limited without consuming a token
|
|
45
|
+
*/
|
|
46
|
+
isRateLimited(): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Get rate limiter statistics
|
|
49
|
+
*/
|
|
50
|
+
stats(): {
|
|
51
|
+
tokens: number;
|
|
52
|
+
maxTokens: number;
|
|
53
|
+
waitTime: number;
|
|
54
|
+
};
|
|
55
|
+
private sleep;
|
|
56
|
+
}
|
|
57
|
+
export declare function getRateLimiter(options?: Partial<RateLimiterOptions>): RateLimiter;
|
|
58
|
+
export declare function resetRateLimiter(): void;
|