Files
zCode-CLI-X/~/.npm-cache/@growthbook/growthbook@1.6.5@@@1/dist/esm/sticky-bucket-service.mjs
admin 875c7f9b91 feat: Complete zCode CLI X with Telegram bot integration
- Add full Telegram bot functionality with Z.AI API integration
- Implement 4 tools: Bash, FileEdit, WebSearch, Git
- Add 3 agents: Code Reviewer, Architect, DevOps Engineer
- Add 6 skills for common coding tasks
- Add systemd service file for 24/7 operation
- Add nginx configuration for HTTPS webhook
- Add comprehensive documentation
- Implement WebSocket server for real-time updates
- Add logging system with Winston
- Add environment validation

🤖 zCode CLI X - Agentic coder with Z.AI + Telegram integration
2026-05-05 09:01:26 +00:00

210 lines
6.7 KiB
JavaScript

import { toString } from "./util.mjs";
import { getStickyBucketAttributeKey } from "./core.mjs";
/**
* Responsible for reading and writing documents which describe sticky bucket assignments.
*/
export class StickyBucketService {
constructor(opts) {
opts = opts || {};
this.prefix = opts.prefix || "";
}
/**
* The SDK calls getAllAssignments to populate sticky buckets. This in turn will
* typically loop through individual getAssignments calls. However, some StickyBucketService
* instances (i.e. Redis) will instead perform a multi-query inside getAllAssignments instead.
*/
async getAllAssignments(attributes) {
const docs = {};
(await Promise.all(Object.entries(attributes).map(([attributeName, attributeValue]) => this.getAssignments(attributeName, attributeValue)))).forEach(doc => {
if (doc) {
const key = getStickyBucketAttributeKey(doc.attributeName, doc.attributeValue);
docs[key] = doc;
}
});
return docs;
}
getKey(attributeName, attributeValue) {
return `${this.prefix}${attributeName}||${attributeValue}`;
}
}
export class StickyBucketServiceSync extends StickyBucketService {
async getAssignments(attributeName, attributeValue) {
return this.getAssignmentsSync(attributeName, attributeValue);
}
async saveAssignments(doc) {
this.saveAssignmentsSync(doc);
}
getAllAssignmentsSync(attributes) {
const docs = {};
Object.entries(attributes).map(([attributeName, attributeValue]) => this.getAssignmentsSync(attributeName, attributeValue)).forEach(doc => {
if (doc) {
const key = getStickyBucketAttributeKey(doc.attributeName, doc.attributeValue);
docs[key] = doc;
}
});
return docs;
}
}
export class LocalStorageStickyBucketService extends StickyBucketService {
constructor(opts) {
opts = opts || {};
super();
this.prefix = opts.prefix || "gbStickyBuckets__";
try {
this.localStorage = opts.localStorage || globalThis.localStorage;
} catch (e) {
// Ignore localStorage errors
}
}
async getAssignments(attributeName, attributeValue) {
const key = this.getKey(attributeName, attributeValue);
let doc = null;
if (!this.localStorage) return doc;
try {
const raw = (await this.localStorage.getItem(key)) || "{}";
const data = JSON.parse(raw);
if (data.attributeName && data.attributeValue && data.assignments) {
doc = data;
}
} catch (e) {
// Ignore localStorage errors
}
return doc;
}
async saveAssignments(doc) {
const key = this.getKey(doc.attributeName, doc.attributeValue);
if (!this.localStorage) return;
try {
await this.localStorage.setItem(key, JSON.stringify(doc));
} catch (e) {
// Ignore localStorage errors
}
}
}
export class ExpressCookieStickyBucketService extends StickyBucketServiceSync {
/**
* Intended to be used with cookieParser() middleware from npm: 'cookie-parser'.
* Assumes:
* - reading a cookie is automatically decoded via decodeURIComponent() or similar
* - writing a cookie name & value must be manually encoded via encodeURIComponent() or similar
* - all cookie bodies are JSON encoded strings and are manually encoded/decoded
*/
constructor({
prefix = "gbStickyBuckets__",
req,
res,
cookieAttributes = {
maxAge: 180 * 24 * 3600 * 1000
} // 180 days
}) {
super();
this.prefix = prefix;
this.req = req;
this.res = res;
this.cookieAttributes = cookieAttributes;
}
getAssignmentsSync(attributeName, attributeValue) {
const key = this.getKey(attributeName, attributeValue);
let doc = null;
if (!this.req) return doc;
try {
const raw = this.req.cookies[key] || "{}";
const data = JSON.parse(raw);
if (data.attributeName && data.attributeValue && data.assignments) {
doc = data;
}
} catch (e) {
// Ignore cookie errors
}
return doc;
}
saveAssignmentsSync(doc) {
const key = this.getKey(doc.attributeName, doc.attributeValue);
if (!this.res) return;
const str = JSON.stringify(doc);
this.res.cookie(encodeURIComponent(key), encodeURIComponent(str), this.cookieAttributes);
}
}
export class BrowserCookieStickyBucketService extends StickyBucketServiceSync {
/**
* Intended to be used with npm: 'js-cookie'.
* Assumes:
* - reading a cookie is automatically decoded via decodeURIComponent() or similar
* - writing a cookie name & value is automatically encoded via encodeURIComponent() or similar
* - all cookie bodies are JSON encoded strings and are manually encoded/decoded
*/
constructor({
prefix = "gbStickyBuckets__",
jsCookie,
cookieAttributes = {
expires: 180
} // 180 days
}) {
super();
this.prefix = prefix;
this.jsCookie = jsCookie;
this.cookieAttributes = cookieAttributes;
}
getAssignmentsSync(attributeName, attributeValue) {
const key = this.getKey(attributeName, attributeValue);
let doc = null;
if (!this.jsCookie) return doc;
try {
const raw = this.jsCookie.get(key);
const data = JSON.parse(raw || "{}");
if (data.attributeName && data.attributeValue && data.assignments) {
doc = data;
}
} catch (e) {
// Ignore cookie errors
}
return doc;
}
async saveAssignmentsSync(doc) {
const key = this.getKey(doc.attributeName, doc.attributeValue);
if (!this.jsCookie) return;
const str = JSON.stringify(doc);
this.jsCookie.set(key, str, this.cookieAttributes);
}
}
export class RedisStickyBucketService extends StickyBucketService {
/** Intended to be used with npm: 'ioredis'. **/
constructor({
redis
}) {
super();
this.redis = redis;
}
async getAllAssignments(attributes) {
const docs = {};
const keys = Object.entries(attributes).map(([attributeName, attributeValue]) => getStickyBucketAttributeKey(attributeName, attributeValue));
if (!this.redis) return docs;
await this.redis.mget(...keys).then(values => {
values.forEach(raw => {
try {
const data = JSON.parse(raw || "{}");
if (data.attributeName && "attributeValue" in data && data.assignments) {
const key = getStickyBucketAttributeKey(data.attributeName, toString(data.attributeValue));
docs[key] = data;
}
} catch (e) {
// ignore redis doc parse errors
}
});
});
return docs;
}
async getAssignments(_attributeName, _attributeValue) {
// not implemented
return null;
}
async saveAssignments(doc) {
const key = this.getKey(doc.attributeName, doc.attributeValue);
if (!this.redis) return;
await this.redis.set(key, JSON.stringify(doc));
}
}
//# sourceMappingURL=sticky-bucket-service.mjs.map