@ceki/sdk 1.13.0 → 1.14.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/dist/index.cjs CHANGED
@@ -41,19 +41,29 @@ __export(index_exports, {
41
41
  ChatSendFailed: () => ChatSendFailed,
42
42
  Client: () => Client,
43
43
  ConnectionLost: () => ConnectionLost,
44
+ ContractClient: () => ContractClient,
45
+ ContractError: () => ContractError,
44
46
  HumanProfile: () => HumanProfile,
45
47
  Humanizer: () => Humanizer,
46
48
  InsufficientFunds: () => InsufficientFunds,
47
49
  NotOwner: () => NotOwner,
48
50
  ProviderDisconnected: () => ProviderDisconnected,
49
51
  ProviderOffline: () => ProviderOffline,
52
+ ROLE_QA: () => ROLE_QA,
53
+ ROLE_REVIEWER: () => ROLE_REVIEWER,
50
54
  RateLimitExceeded: () => RateLimitExceeded,
51
55
  SessionEnded: () => SessionEnded,
52
56
  SessionExpired: () => SessionExpired,
53
57
  SessionNotFound: () => SessionNotFound,
58
+ TimelogClient: () => TimelogClient,
54
59
  TimeoutError: () => TimeoutError,
55
60
  TransportError: () => TransportError,
56
- connect: () => connect
61
+ cleanArgs: () => cleanArgs,
62
+ connect: () => connect,
63
+ contractIdsFromEnv: () => contractIdsFromEnv,
64
+ deriveLabel: () => deriveLabel,
65
+ parseBenefitable: () => parseBenefitable,
66
+ parseParticipant: () => parseParticipant
57
67
  });
58
68
  module.exports = __toCommonJS(index_exports);
59
69
 
@@ -2049,6 +2059,380 @@ var Client = class _Client {
2049
2059
  async function connect(apiKey, opts) {
2050
2060
  return Client.create(apiKey, opts);
2051
2061
  }
2062
+
2063
+ // src/contract.ts
2064
+ var ROLE_REVIEWER = 5;
2065
+ var ROLE_QA = 6;
2066
+ var ContractError = class extends Error {
2067
+ constructor(message) {
2068
+ super(message);
2069
+ this.name = "ContractError";
2070
+ }
2071
+ };
2072
+ function parseBenefitable(value) {
2073
+ if (value === null || value === void 0 || value === "") return null;
2074
+ const parts = String(value).split(":");
2075
+ if (parts.length !== 2) {
2076
+ throw new Error(`benefitable must be 'type:id', got: ${JSON.stringify(value)}`);
2077
+ }
2078
+ const [btype, bid] = parts;
2079
+ const num = Number.parseInt(bid, 10);
2080
+ if (!Number.isFinite(num) || Number.isNaN(num)) {
2081
+ throw new Error(`benefitable id must be int, got: ${JSON.stringify(bid)}`);
2082
+ }
2083
+ return { type: btype, value: num };
2084
+ }
2085
+ function parseParticipant(value, roleId) {
2086
+ const base = parseBenefitable(value);
2087
+ if (base === null) return null;
2088
+ return {
2089
+ participable_id: base.value,
2090
+ type: base.type,
2091
+ role_id: roleId
2092
+ };
2093
+ }
2094
+ function cleanArgs(o) {
2095
+ const out = {};
2096
+ for (const [k, v] of Object.entries(o)) {
2097
+ if (v !== void 0 && v !== null) {
2098
+ out[k] = v;
2099
+ }
2100
+ }
2101
+ return out;
2102
+ }
2103
+ function deriveLabel(desc) {
2104
+ if (!desc) return "progress";
2105
+ const lines = String(desc).split(/\r?\n/);
2106
+ for (const ln of lines) {
2107
+ const t = ln.trim();
2108
+ if (t) return t.slice(0, 60);
2109
+ }
2110
+ return "progress";
2111
+ }
2112
+ function contractIdsFromEnv() {
2113
+ const raw = (process.env.CEKI_CONTRACT_IDS ?? "").trim();
2114
+ if (!raw) return [];
2115
+ try {
2116
+ const parsed = JSON.parse(raw);
2117
+ if (Array.isArray(parsed)) return parsed.map((x) => String(x));
2118
+ } catch {
2119
+ }
2120
+ return raw.replace(/\[/g, "").replace(/\]/g, "").split(",").map((s) => s.trim()).filter((s) => s.length > 0);
2121
+ }
2122
+ function resolveEndpoint() {
2123
+ const override = process.env.CEKI_AGENT_MCP_ENDPOINT;
2124
+ if (override) return override.replace(/\/+$/, "");
2125
+ const base = (process.env.CEKI_API_URL ?? defaults.apiUrl).replace(/\/+$/, "");
2126
+ return `${base}/mcp/agent`;
2127
+ }
2128
+ function resolveApiBase() {
2129
+ const override = process.env.CEKI_API_BASE;
2130
+ if (override) return override.replace(/\/+$/, "");
2131
+ const base = (process.env.CEKI_API_URL ?? defaults.apiUrl).replace(/\/+$/, "");
2132
+ return `${base}/api`;
2133
+ }
2134
+ function resolveToken() {
2135
+ return process.env.CEKI_AGENT_TOKEN ?? process.env.CEKI_API_KEY ?? "";
2136
+ }
2137
+ var TOOL_MAP = {
2138
+ list: "get-my-contracts",
2139
+ members: "get-contract-members",
2140
+ tasks: "get-contract-events",
2141
+ "my-jobs": "get-my-jobs",
2142
+ task: "get-event",
2143
+ children: "get-event-children",
2144
+ history: "get-event-history",
2145
+ create: "create-contract-event",
2146
+ comment: "comment",
2147
+ propose: "propose-correction",
2148
+ vote: "vote-correction"
2149
+ };
2150
+ var FetchHttpClient = class {
2151
+ constructor(timeoutMs) {
2152
+ this.timeoutMs = timeoutMs;
2153
+ }
2154
+ timeoutMs;
2155
+ withTimeout() {
2156
+ const ctl = new AbortController();
2157
+ const t = setTimeout(() => ctl.abort(), this.timeoutMs);
2158
+ return { signal: ctl.signal, cancel: () => clearTimeout(t) };
2159
+ }
2160
+ async post(url, init) {
2161
+ const { signal, cancel } = this.withTimeout();
2162
+ try {
2163
+ const r = await fetch(url, { method: "POST", headers: init.headers, body: init.body, signal });
2164
+ const text = await r.text();
2165
+ return {
2166
+ status: r.status,
2167
+ text: async () => text,
2168
+ json: async () => {
2169
+ try {
2170
+ return JSON.parse(text);
2171
+ } catch {
2172
+ return { raw: text };
2173
+ }
2174
+ }
2175
+ };
2176
+ } finally {
2177
+ cancel();
2178
+ }
2179
+ }
2180
+ async get(url, init) {
2181
+ const { signal, cancel } = this.withTimeout();
2182
+ try {
2183
+ const r = await fetch(url, { method: "GET", headers: init.headers, signal });
2184
+ const text = await r.text();
2185
+ return {
2186
+ status: r.status,
2187
+ text: async () => text,
2188
+ json: async () => {
2189
+ try {
2190
+ return JSON.parse(text);
2191
+ } catch {
2192
+ return { raw: text };
2193
+ }
2194
+ }
2195
+ };
2196
+ } finally {
2197
+ cancel();
2198
+ }
2199
+ }
2200
+ };
2201
+ var ContractClient = class {
2202
+ endpoint;
2203
+ apiBase;
2204
+ token;
2205
+ http;
2206
+ constructor(opts = {}) {
2207
+ this.endpoint = (opts.endpoint ?? resolveEndpoint()).replace(/\/+$/, "");
2208
+ this.apiBase = (opts.apiBase ?? resolveApiBase()).replace(/\/+$/, "");
2209
+ this.token = opts.token !== void 0 ? opts.token : resolveToken();
2210
+ this.http = opts.http ?? new FetchHttpClient(opts.timeoutMs ?? 3e4);
2211
+ }
2212
+ headers() {
2213
+ if (!this.token) {
2214
+ throw new ContractError("agent token not set (CEKI_AGENT_TOKEN or CEKI_API_KEY)");
2215
+ }
2216
+ return {
2217
+ "Content-Type": "application/json",
2218
+ Accept: "application/json",
2219
+ Authorization: `Bearer ${this.token}`
2220
+ };
2221
+ }
2222
+ async rpc(method, params) {
2223
+ const body = JSON.stringify({
2224
+ jsonrpc: "2.0",
2225
+ id: Date.now(),
2226
+ method,
2227
+ params
2228
+ });
2229
+ const resp = await this.http.post(this.endpoint, { headers: this.headers(), body });
2230
+ let parsed;
2231
+ try {
2232
+ parsed = await resp.json();
2233
+ } catch {
2234
+ parsed = { raw: await resp.text() };
2235
+ }
2236
+ if (resp.status !== 200) {
2237
+ const snippet = JSON.stringify(parsed).slice(0, 400);
2238
+ throw new ContractError(`HTTP ${resp.status}: ${snippet}`);
2239
+ }
2240
+ return parsed ?? {};
2241
+ }
2242
+ /** Call MCP tool; unwrap content[].text (JSON-parsed) or structuredContent. */
2243
+ async call(tool, args = {}) {
2244
+ const body = await this.rpc("tools/call", { name: tool, arguments: args });
2245
+ if (body["error"]) {
2246
+ throw new ContractError(`${tool} \u2192 ${JSON.stringify(body["error"]).slice(0, 400)}`);
2247
+ }
2248
+ const result = body["result"] ?? {};
2249
+ const content = result["content"];
2250
+ if (Array.isArray(content)) {
2251
+ const texts = content.filter((c) => c["type"] === "text").map((c) => String(c["text"] ?? ""));
2252
+ const joined = texts.join("\n");
2253
+ try {
2254
+ return JSON.parse(joined);
2255
+ } catch {
2256
+ return joined;
2257
+ }
2258
+ }
2259
+ if (result["structuredContent"] !== void 0) return result["structuredContent"];
2260
+ return result;
2261
+ }
2262
+ async tools() {
2263
+ const body = await this.rpc("tools/list", {});
2264
+ const result = body["result"] ?? {};
2265
+ const tools = result["tools"];
2266
+ if (Array.isArray(tools)) return tools.map((t) => t["name"]);
2267
+ return body;
2268
+ }
2269
+ async raw(tool, args = {}) {
2270
+ return this.call(tool, args);
2271
+ }
2272
+ // ── domain helpers ──────────────────────────────────────────────
2273
+ async listContracts() {
2274
+ return this.call(TOOL_MAP.list, {});
2275
+ }
2276
+ async members(contractId) {
2277
+ return this.call(TOOL_MAP.members, { contract_id: Number(contractId) });
2278
+ }
2279
+ async tasks(contractId) {
2280
+ return this.call(TOOL_MAP.tasks, { contract_id: Number(contractId) });
2281
+ }
2282
+ async myJobs() {
2283
+ return this.call(TOOL_MAP["my-jobs"], {});
2284
+ }
2285
+ async task(eventId) {
2286
+ return this.call(TOOL_MAP.task, { event_id: Number(eventId) });
2287
+ }
2288
+ async children(eventId) {
2289
+ return this.call(TOOL_MAP.children, { event_id: Number(eventId) });
2290
+ }
2291
+ async history(eventId, opts = {}) {
2292
+ const args = cleanArgs({ event_id: Number(eventId), limit: opts.limit });
2293
+ return this.call(TOOL_MAP.history, args);
2294
+ }
2295
+ async create(contractId, opts) {
2296
+ const users = [];
2297
+ const rev = parseParticipant(opts.reviewer, ROLE_REVIEWER);
2298
+ if (rev !== null) users.push(rev);
2299
+ const qa = parseParticipant(opts.qa, ROLE_QA);
2300
+ if (qa !== null) users.push(qa);
2301
+ if (opts.participants && opts.participants.length) {
2302
+ users.push(...opts.participants);
2303
+ }
2304
+ const args = cleanArgs({
2305
+ contract_id: Number(contractId),
2306
+ label: opts.label,
2307
+ type_id: opts.type,
2308
+ status_id: opts.status,
2309
+ kal_schedule_id: opts.kalScheduleId,
2310
+ start: opts.start,
2311
+ end: opts.end,
2312
+ timezone: opts.timezone,
2313
+ date: opts.date,
2314
+ duration: opts.duration,
2315
+ amount: opts.amount,
2316
+ currency: opts.currency,
2317
+ description: opts.description,
2318
+ data: opts.data,
2319
+ benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0,
2320
+ users: users.length > 0 ? users : void 0
2321
+ });
2322
+ return this.call(TOOL_MAP.create, args);
2323
+ }
2324
+ async comment(eventId, opts = {}) {
2325
+ const args = cleanArgs({
2326
+ event_id: Number(eventId),
2327
+ label: opts.label,
2328
+ type_id: opts.type,
2329
+ status_id: opts.status,
2330
+ start: opts.start,
2331
+ end: opts.end,
2332
+ date: opts.date,
2333
+ duration: opts.duration,
2334
+ amount: opts.amount,
2335
+ currency: opts.currency,
2336
+ description: opts.description,
2337
+ benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0
2338
+ });
2339
+ return this.call(TOOL_MAP.comment, args);
2340
+ }
2341
+ async propose(eventId, opts = {}) {
2342
+ const args = cleanArgs({
2343
+ event_id: Number(eventId),
2344
+ status_id: opts.status,
2345
+ label: opts.label,
2346
+ description: opts.description,
2347
+ start: opts.start,
2348
+ end: opts.end,
2349
+ date: opts.date,
2350
+ duration: opts.duration,
2351
+ amount: opts.amount,
2352
+ currency: opts.currency,
2353
+ benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0
2354
+ });
2355
+ return this.call(TOOL_MAP.propose, args);
2356
+ }
2357
+ /** Status correction (optional) + progress comment in one shot.
2358
+ *
2359
+ * The event's own description is NOT touched. `desc` becomes the
2360
+ * body of a child comment-event, not a label/description overwrite
2361
+ * on the parent event. Use this for Hand/QA/Reviewer progress
2362
+ * reports — `propose --desc` would clobber the parent spec.
2363
+ */
2364
+ async progress(eventId, opts) {
2365
+ let statusResult = null;
2366
+ if (opts.status !== void 0 && opts.status !== null) {
2367
+ statusResult = await this.propose(eventId, { status: Number(opts.status) });
2368
+ }
2369
+ const label = deriveLabel(opts.desc);
2370
+ const commentResult = await this.comment(eventId, { label, description: opts.desc });
2371
+ return { status_correction: statusResult, comment: commentResult };
2372
+ }
2373
+ async vote(eventId, ids, vote) {
2374
+ return this.call(TOOL_MAP.vote, {
2375
+ event_id: Number(eventId),
2376
+ ids: ids.map((i) => Number(i)),
2377
+ vote: Boolean(vote)
2378
+ });
2379
+ }
2380
+ // ── polling (REST, not MCP) ────────────────────────────────────
2381
+ /** GET /agent/polling. Returns [] on 429 (rate-limit, 10/min/token). */
2382
+ async poll() {
2383
+ const resp = await this.http.get(`${this.apiBase}/agent/polling`, {
2384
+ headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` }
2385
+ });
2386
+ if (resp.status === 429) return [];
2387
+ if (resp.status !== 200) {
2388
+ let body2;
2389
+ try {
2390
+ body2 = await resp.json();
2391
+ } catch {
2392
+ body2 = await resp.text();
2393
+ }
2394
+ throw new ContractError(`polling HTTP ${resp.status}: ${JSON.stringify(body2).slice(0, 300)}`);
2395
+ }
2396
+ const body = await resp.json();
2397
+ if (Array.isArray(body)) return body;
2398
+ if (body && typeof body === "object") {
2399
+ const obj = body;
2400
+ for (const k of ["notifications", "data", "items"]) {
2401
+ const v = obj[k];
2402
+ if (Array.isArray(v)) return v;
2403
+ }
2404
+ }
2405
+ return [];
2406
+ }
2407
+ };
2408
+
2409
+ // src/timelog.ts
2410
+ var TOOL_MAP2 = {
2411
+ start: "timelog-start",
2412
+ stop: "timelog-stop",
2413
+ check: "timelog-check"
2414
+ };
2415
+ var TimelogClient = class {
2416
+ c;
2417
+ constructor(opts = {}) {
2418
+ if (opts.contract) {
2419
+ this.c = opts.contract;
2420
+ } else {
2421
+ this.c = new ContractClient(opts);
2422
+ }
2423
+ }
2424
+ async start(eventId) {
2425
+ return this.c.call(TOOL_MAP2.start, { event_id: Number(eventId) });
2426
+ }
2427
+ async stop(eventId, label) {
2428
+ const args = { event_id: Number(eventId) };
2429
+ if (label !== void 0 && label !== null) args["label"] = label;
2430
+ return this.c.call(TOOL_MAP2.stop, args);
2431
+ }
2432
+ async check(eventId) {
2433
+ return this.c.call(TOOL_MAP2.check, { event_id: Number(eventId) });
2434
+ }
2435
+ };
2052
2436
  // Annotate the CommonJS export names for ESM import in node:
2053
2437
  0 && (module.exports = {
2054
2438
  AuthError,
@@ -2062,18 +2446,28 @@ async function connect(apiKey, opts) {
2062
2446
  ChatSendFailed,
2063
2447
  Client,
2064
2448
  ConnectionLost,
2449
+ ContractClient,
2450
+ ContractError,
2065
2451
  HumanProfile,
2066
2452
  Humanizer,
2067
2453
  InsufficientFunds,
2068
2454
  NotOwner,
2069
2455
  ProviderDisconnected,
2070
2456
  ProviderOffline,
2457
+ ROLE_QA,
2458
+ ROLE_REVIEWER,
2071
2459
  RateLimitExceeded,
2072
2460
  SessionEnded,
2073
2461
  SessionExpired,
2074
2462
  SessionNotFound,
2463
+ TimelogClient,
2075
2464
  TimeoutError,
2076
2465
  TransportError,
2077
- connect
2466
+ cleanArgs,
2467
+ connect,
2468
+ contractIdsFromEnv,
2469
+ deriveLabel,
2470
+ parseBenefitable,
2471
+ parseParticipant
2078
2472
  });
2079
2473
  //# sourceMappingURL=index.cjs.map