@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/cli.js CHANGED
@@ -472,8 +472,8 @@ function saveSession(sid, data) {
472
472
  function deleteSession(sid) {
473
473
  try {
474
474
  fs2.unlinkSync(statePath(sid));
475
- } catch (err2) {
476
- if (err2.code !== "ENOENT") throw err2;
475
+ } catch (err3) {
476
+ if (err3.code !== "ENOENT") throw err3;
477
477
  }
478
478
  }
479
479
  function getLastSeenTs(sid) {
@@ -1189,10 +1189,10 @@ var Browser = class _Browser {
1189
1189
  _onChatSendError(msg) {
1190
1190
  this.chat._onSendError(msg);
1191
1191
  }
1192
- _rejectAllPending(err2) {
1192
+ _rejectAllPending(err3) {
1193
1193
  for (const [id, pending] of this._pendingCdp) {
1194
1194
  clearTimeout(pending.timer);
1195
- pending.reject(err2);
1195
+ pending.reject(err3);
1196
1196
  }
1197
1197
  this._pendingCdp.clear();
1198
1198
  }
@@ -1604,9 +1604,9 @@ var Client = class _Client {
1604
1604
  this._stopHeartbeat();
1605
1605
  const reasonStr = reason.toString();
1606
1606
  if (code === 4401 || code === 4403) {
1607
- const err2 = new AuthError(reasonStr || `Auth failed (${code})`);
1607
+ const err3 = new AuthError(reasonStr || `Auth failed (${code})`);
1608
1608
  if (this._connectReject) {
1609
- this._connectReject(err2);
1609
+ this._connectReject(err3);
1610
1610
  this._connectResolve = null;
1611
1611
  this._connectReject = null;
1612
1612
  }
@@ -1616,9 +1616,9 @@ var Client = class _Client {
1616
1616
  this._scheduleReconnect();
1617
1617
  }
1618
1618
  });
1619
- this._ws.on("error", (err2) => {
1619
+ this._ws.on("error", (err3) => {
1620
1620
  if (this._connectReject) {
1621
- this._connectReject(new TransportError(err2.message));
1621
+ this._connectReject(new TransportError(err3.message));
1622
1622
  this._connectResolve = null;
1623
1623
  this._connectReject = null;
1624
1624
  }
@@ -1637,8 +1637,8 @@ var Client = class _Client {
1637
1637
  _scheduleReconnect() {
1638
1638
  if (this._closed || this._reconnecting) return;
1639
1639
  if (this._reconnectAttempt >= MAX_RECONNECT_ATTEMPTS) {
1640
- const err2 = new ConnectionLost("Max reconnection attempts exceeded");
1641
- this._rejectAllPending(err2);
1640
+ const err3 = new ConnectionLost("Max reconnection attempts exceeded");
1641
+ this._rejectAllPending(err3);
1642
1642
  return;
1643
1643
  }
1644
1644
  this._reconnecting = true;
@@ -1929,44 +1929,44 @@ var Client = class _Client {
1929
1929
  }
1930
1930
  }
1931
1931
  }
1932
- let err2;
1932
+ let err3;
1933
1933
  switch (code) {
1934
1934
  case -1012:
1935
- err2 = new InsufficientFunds(reason);
1936
- this._rejectAllPending(err2);
1935
+ err3 = new InsufficientFunds(reason);
1936
+ this._rejectAllPending(err3);
1937
1937
  break;
1938
1938
  case -1013:
1939
- err2 = new RateLimitExceeded(0, reason);
1940
- this._rejectAllPending(err2);
1939
+ err3 = new RateLimitExceeded(0, reason);
1940
+ this._rejectAllPending(err3);
1941
1941
  break;
1942
1942
  case -1015:
1943
- err2 = new ProviderOffline(reason);
1944
- this._rejectFirstPendingRent(err2);
1943
+ err3 = new ProviderOffline(reason);
1944
+ this._rejectFirstPendingRent(err3);
1945
1945
  break;
1946
1946
  default:
1947
- err2 = new CekiBrowserError(reason || `relay error ${code}`);
1948
- this._rejectFirstPendingRent(err2);
1947
+ err3 = new CekiBrowserError(reason || `relay error ${code}`);
1948
+ this._rejectFirstPendingRent(err3);
1949
1949
  break;
1950
1950
  }
1951
1951
  }
1952
- _rejectFirstPendingRent(err2) {
1952
+ _rejectFirstPendingRent(err3) {
1953
1953
  const eventId = this._pendingRents.keys().next().value;
1954
1954
  if (eventId != null) {
1955
1955
  const pending = this._pendingRents.get(eventId);
1956
1956
  clearTimeout(pending.timer);
1957
1957
  this._pendingRents.delete(eventId);
1958
- pending.reject(err2);
1958
+ pending.reject(err3);
1959
1959
  }
1960
1960
  }
1961
- _rejectAllPending(err2) {
1961
+ _rejectAllPending(err3) {
1962
1962
  for (const [key, pending] of this._pendingRents) {
1963
1963
  clearTimeout(pending.timer);
1964
- pending.reject(err2);
1964
+ pending.reject(err3);
1965
1965
  }
1966
1966
  this._pendingRents.clear();
1967
1967
  for (const [key, pending] of this._pendingResumes) {
1968
1968
  clearTimeout(pending.timer);
1969
- pending.reject(err2);
1969
+ pending.reject(err3);
1970
1970
  }
1971
1971
  this._pendingResumes.clear();
1972
1972
  }
@@ -1990,17 +1990,796 @@ async function connect(apiKey, opts) {
1990
1990
  return Client.create(apiKey, opts);
1991
1991
  }
1992
1992
 
1993
- // src/cli.ts
1993
+ // src/contract.ts
1994
+ var ROLE_REVIEWER = 5;
1995
+ var ROLE_QA = 6;
1996
+ var ContractError = class extends Error {
1997
+ constructor(message) {
1998
+ super(message);
1999
+ this.name = "ContractError";
2000
+ }
2001
+ };
2002
+ function parseBenefitable(value) {
2003
+ if (value === null || value === void 0 || value === "") return null;
2004
+ const parts = String(value).split(":");
2005
+ if (parts.length !== 2) {
2006
+ throw new Error(`benefitable must be 'type:id', got: ${JSON.stringify(value)}`);
2007
+ }
2008
+ const [btype, bid] = parts;
2009
+ const num = Number.parseInt(bid, 10);
2010
+ if (!Number.isFinite(num) || Number.isNaN(num)) {
2011
+ throw new Error(`benefitable id must be int, got: ${JSON.stringify(bid)}`);
2012
+ }
2013
+ return { type: btype, value: num };
2014
+ }
2015
+ function parseParticipant(value, roleId) {
2016
+ const base = parseBenefitable(value);
2017
+ if (base === null) return null;
2018
+ return {
2019
+ participable_id: base.value,
2020
+ type: base.type,
2021
+ role_id: roleId
2022
+ };
2023
+ }
2024
+ function cleanArgs(o) {
2025
+ const out3 = {};
2026
+ for (const [k, v] of Object.entries(o)) {
2027
+ if (v !== void 0 && v !== null) {
2028
+ out3[k] = v;
2029
+ }
2030
+ }
2031
+ return out3;
2032
+ }
2033
+ function deriveLabel(desc) {
2034
+ if (!desc) return "progress";
2035
+ const lines = String(desc).split(/\r?\n/);
2036
+ for (const ln of lines) {
2037
+ const t = ln.trim();
2038
+ if (t) return t.slice(0, 60);
2039
+ }
2040
+ return "progress";
2041
+ }
2042
+ function contractIdsFromEnv() {
2043
+ const raw = (process.env.CEKI_CONTRACT_IDS ?? "").trim();
2044
+ if (!raw) return [];
2045
+ try {
2046
+ const parsed = JSON.parse(raw);
2047
+ if (Array.isArray(parsed)) return parsed.map((x) => String(x));
2048
+ } catch {
2049
+ }
2050
+ return raw.replace(/\[/g, "").replace(/\]/g, "").split(",").map((s) => s.trim()).filter((s) => s.length > 0);
2051
+ }
2052
+ function resolveEndpoint() {
2053
+ const override = process.env.CEKI_AGENT_MCP_ENDPOINT;
2054
+ if (override) return override.replace(/\/+$/, "");
2055
+ const base = (process.env.CEKI_API_URL ?? defaults.apiUrl).replace(/\/+$/, "");
2056
+ return `${base}/mcp/agent`;
2057
+ }
2058
+ function resolveApiBase() {
2059
+ const override = process.env.CEKI_API_BASE;
2060
+ if (override) return override.replace(/\/+$/, "");
2061
+ const base = (process.env.CEKI_API_URL ?? defaults.apiUrl).replace(/\/+$/, "");
2062
+ return `${base}/api`;
2063
+ }
2064
+ function resolveToken() {
2065
+ return process.env.CEKI_AGENT_TOKEN ?? process.env.CEKI_API_KEY ?? "";
2066
+ }
2067
+ var TOOL_MAP = {
2068
+ list: "get-my-contracts",
2069
+ members: "get-contract-members",
2070
+ tasks: "get-contract-events",
2071
+ "my-jobs": "get-my-jobs",
2072
+ task: "get-event",
2073
+ children: "get-event-children",
2074
+ history: "get-event-history",
2075
+ create: "create-contract-event",
2076
+ comment: "comment",
2077
+ propose: "propose-correction",
2078
+ vote: "vote-correction"
2079
+ };
2080
+ var FetchHttpClient = class {
2081
+ constructor(timeoutMs) {
2082
+ this.timeoutMs = timeoutMs;
2083
+ }
2084
+ timeoutMs;
2085
+ withTimeout() {
2086
+ const ctl = new AbortController();
2087
+ const t = setTimeout(() => ctl.abort(), this.timeoutMs);
2088
+ return { signal: ctl.signal, cancel: () => clearTimeout(t) };
2089
+ }
2090
+ async post(url, init) {
2091
+ const { signal, cancel } = this.withTimeout();
2092
+ try {
2093
+ const r = await fetch(url, { method: "POST", headers: init.headers, body: init.body, signal });
2094
+ const text = await r.text();
2095
+ return {
2096
+ status: r.status,
2097
+ text: async () => text,
2098
+ json: async () => {
2099
+ try {
2100
+ return JSON.parse(text);
2101
+ } catch {
2102
+ return { raw: text };
2103
+ }
2104
+ }
2105
+ };
2106
+ } finally {
2107
+ cancel();
2108
+ }
2109
+ }
2110
+ async get(url, init) {
2111
+ const { signal, cancel } = this.withTimeout();
2112
+ try {
2113
+ const r = await fetch(url, { method: "GET", headers: init.headers, signal });
2114
+ const text = await r.text();
2115
+ return {
2116
+ status: r.status,
2117
+ text: async () => text,
2118
+ json: async () => {
2119
+ try {
2120
+ return JSON.parse(text);
2121
+ } catch {
2122
+ return { raw: text };
2123
+ }
2124
+ }
2125
+ };
2126
+ } finally {
2127
+ cancel();
2128
+ }
2129
+ }
2130
+ };
2131
+ var ContractClient = class {
2132
+ endpoint;
2133
+ apiBase;
2134
+ token;
2135
+ http;
2136
+ constructor(opts = {}) {
2137
+ this.endpoint = (opts.endpoint ?? resolveEndpoint()).replace(/\/+$/, "");
2138
+ this.apiBase = (opts.apiBase ?? resolveApiBase()).replace(/\/+$/, "");
2139
+ this.token = opts.token !== void 0 ? opts.token : resolveToken();
2140
+ this.http = opts.http ?? new FetchHttpClient(opts.timeoutMs ?? 3e4);
2141
+ }
2142
+ headers() {
2143
+ if (!this.token) {
2144
+ throw new ContractError("agent token not set (CEKI_AGENT_TOKEN or CEKI_API_KEY)");
2145
+ }
2146
+ return {
2147
+ "Content-Type": "application/json",
2148
+ Accept: "application/json",
2149
+ Authorization: `Bearer ${this.token}`
2150
+ };
2151
+ }
2152
+ async rpc(method, params) {
2153
+ const body = JSON.stringify({
2154
+ jsonrpc: "2.0",
2155
+ id: Date.now(),
2156
+ method,
2157
+ params
2158
+ });
2159
+ const resp = await this.http.post(this.endpoint, { headers: this.headers(), body });
2160
+ let parsed;
2161
+ try {
2162
+ parsed = await resp.json();
2163
+ } catch {
2164
+ parsed = { raw: await resp.text() };
2165
+ }
2166
+ if (resp.status !== 200) {
2167
+ const snippet = JSON.stringify(parsed).slice(0, 400);
2168
+ throw new ContractError(`HTTP ${resp.status}: ${snippet}`);
2169
+ }
2170
+ return parsed ?? {};
2171
+ }
2172
+ /** Call MCP tool; unwrap content[].text (JSON-parsed) or structuredContent. */
2173
+ async call(tool, args = {}) {
2174
+ const body = await this.rpc("tools/call", { name: tool, arguments: args });
2175
+ if (body["error"]) {
2176
+ throw new ContractError(`${tool} \u2192 ${JSON.stringify(body["error"]).slice(0, 400)}`);
2177
+ }
2178
+ const result = body["result"] ?? {};
2179
+ const content = result["content"];
2180
+ if (Array.isArray(content)) {
2181
+ const texts = content.filter((c) => c["type"] === "text").map((c) => String(c["text"] ?? ""));
2182
+ const joined = texts.join("\n");
2183
+ try {
2184
+ return JSON.parse(joined);
2185
+ } catch {
2186
+ return joined;
2187
+ }
2188
+ }
2189
+ if (result["structuredContent"] !== void 0) return result["structuredContent"];
2190
+ return result;
2191
+ }
2192
+ async tools() {
2193
+ const body = await this.rpc("tools/list", {});
2194
+ const result = body["result"] ?? {};
2195
+ const tools = result["tools"];
2196
+ if (Array.isArray(tools)) return tools.map((t) => t["name"]);
2197
+ return body;
2198
+ }
2199
+ async raw(tool, args = {}) {
2200
+ return this.call(tool, args);
2201
+ }
2202
+ // ── domain helpers ──────────────────────────────────────────────
2203
+ async listContracts() {
2204
+ return this.call(TOOL_MAP.list, {});
2205
+ }
2206
+ async members(contractId) {
2207
+ return this.call(TOOL_MAP.members, { contract_id: Number(contractId) });
2208
+ }
2209
+ async tasks(contractId) {
2210
+ return this.call(TOOL_MAP.tasks, { contract_id: Number(contractId) });
2211
+ }
2212
+ async myJobs() {
2213
+ return this.call(TOOL_MAP["my-jobs"], {});
2214
+ }
2215
+ async task(eventId) {
2216
+ return this.call(TOOL_MAP.task, { event_id: Number(eventId) });
2217
+ }
2218
+ async children(eventId) {
2219
+ return this.call(TOOL_MAP.children, { event_id: Number(eventId) });
2220
+ }
2221
+ async history(eventId, opts = {}) {
2222
+ const args = cleanArgs({ event_id: Number(eventId), limit: opts.limit });
2223
+ return this.call(TOOL_MAP.history, args);
2224
+ }
2225
+ async create(contractId, opts) {
2226
+ const users = [];
2227
+ const rev = parseParticipant(opts.reviewer, ROLE_REVIEWER);
2228
+ if (rev !== null) users.push(rev);
2229
+ const qa = parseParticipant(opts.qa, ROLE_QA);
2230
+ if (qa !== null) users.push(qa);
2231
+ if (opts.participants && opts.participants.length) {
2232
+ users.push(...opts.participants);
2233
+ }
2234
+ const args = cleanArgs({
2235
+ contract_id: Number(contractId),
2236
+ label: opts.label,
2237
+ type_id: opts.type,
2238
+ status_id: opts.status,
2239
+ kal_schedule_id: opts.kalScheduleId,
2240
+ start: opts.start,
2241
+ end: opts.end,
2242
+ timezone: opts.timezone,
2243
+ date: opts.date,
2244
+ duration: opts.duration,
2245
+ amount: opts.amount,
2246
+ currency: opts.currency,
2247
+ description: opts.description,
2248
+ data: opts.data,
2249
+ benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0,
2250
+ users: users.length > 0 ? users : void 0
2251
+ });
2252
+ return this.call(TOOL_MAP.create, args);
2253
+ }
2254
+ async comment(eventId, opts = {}) {
2255
+ const args = cleanArgs({
2256
+ event_id: Number(eventId),
2257
+ label: opts.label,
2258
+ type_id: opts.type,
2259
+ status_id: opts.status,
2260
+ start: opts.start,
2261
+ end: opts.end,
2262
+ date: opts.date,
2263
+ duration: opts.duration,
2264
+ amount: opts.amount,
2265
+ currency: opts.currency,
2266
+ description: opts.description,
2267
+ benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0
2268
+ });
2269
+ return this.call(TOOL_MAP.comment, args);
2270
+ }
2271
+ async propose(eventId, opts = {}) {
2272
+ const args = cleanArgs({
2273
+ event_id: Number(eventId),
2274
+ status_id: opts.status,
2275
+ label: opts.label,
2276
+ description: opts.description,
2277
+ start: opts.start,
2278
+ end: opts.end,
2279
+ date: opts.date,
2280
+ duration: opts.duration,
2281
+ amount: opts.amount,
2282
+ currency: opts.currency,
2283
+ benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0
2284
+ });
2285
+ return this.call(TOOL_MAP.propose, args);
2286
+ }
2287
+ /** Status correction (optional) + progress comment in one shot.
2288
+ *
2289
+ * The event's own description is NOT touched. `desc` becomes the
2290
+ * body of a child comment-event, not a label/description overwrite
2291
+ * on the parent event. Use this for Hand/QA/Reviewer progress
2292
+ * reports — `propose --desc` would clobber the parent spec.
2293
+ */
2294
+ async progress(eventId, opts) {
2295
+ let statusResult = null;
2296
+ if (opts.status !== void 0 && opts.status !== null) {
2297
+ statusResult = await this.propose(eventId, { status: Number(opts.status) });
2298
+ }
2299
+ const label = deriveLabel(opts.desc);
2300
+ const commentResult = await this.comment(eventId, { label, description: opts.desc });
2301
+ return { status_correction: statusResult, comment: commentResult };
2302
+ }
2303
+ async vote(eventId, ids, vote) {
2304
+ return this.call(TOOL_MAP.vote, {
2305
+ event_id: Number(eventId),
2306
+ ids: ids.map((i) => Number(i)),
2307
+ vote: Boolean(vote)
2308
+ });
2309
+ }
2310
+ // ── polling (REST, not MCP) ────────────────────────────────────
2311
+ /** GET /agent/polling. Returns [] on 429 (rate-limit, 10/min/token). */
2312
+ async poll() {
2313
+ const resp = await this.http.get(`${this.apiBase}/agent/polling`, {
2314
+ headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` }
2315
+ });
2316
+ if (resp.status === 429) return [];
2317
+ if (resp.status !== 200) {
2318
+ let body2;
2319
+ try {
2320
+ body2 = await resp.json();
2321
+ } catch {
2322
+ body2 = await resp.text();
2323
+ }
2324
+ throw new ContractError(`polling HTTP ${resp.status}: ${JSON.stringify(body2).slice(0, 300)}`);
2325
+ }
2326
+ const body = await resp.json();
2327
+ if (Array.isArray(body)) return body;
2328
+ if (body && typeof body === "object") {
2329
+ const obj = body;
2330
+ for (const k of ["notifications", "data", "items"]) {
2331
+ const v = obj[k];
2332
+ if (Array.isArray(v)) return v;
2333
+ }
2334
+ }
2335
+ return [];
2336
+ }
2337
+ };
2338
+
2339
+ // src/timelog.ts
2340
+ var TOOL_MAP2 = {
2341
+ start: "timelog-start",
2342
+ stop: "timelog-stop",
2343
+ check: "timelog-check"
2344
+ };
2345
+ var TimelogClient = class {
2346
+ c;
2347
+ constructor(opts = {}) {
2348
+ if (opts.contract) {
2349
+ this.c = opts.contract;
2350
+ } else {
2351
+ this.c = new ContractClient(opts);
2352
+ }
2353
+ }
2354
+ async start(eventId) {
2355
+ return this.c.call(TOOL_MAP2.start, { event_id: Number(eventId) });
2356
+ }
2357
+ async stop(eventId, label) {
2358
+ const args = { event_id: Number(eventId) };
2359
+ if (label !== void 0 && label !== null) args["label"] = label;
2360
+ return this.c.call(TOOL_MAP2.stop, args);
2361
+ }
2362
+ async check(eventId) {
2363
+ return this.c.call(TOOL_MAP2.check, { event_id: Number(eventId) });
2364
+ }
2365
+ };
2366
+
2367
+ // src/contract-cli.ts
1994
2368
  function out(data) {
2369
+ if (typeof data === "string") {
2370
+ process.stdout.write(data + "\n");
2371
+ } else {
2372
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
2373
+ }
2374
+ }
2375
+ function err(message, code = "error") {
2376
+ process.stderr.write(JSON.stringify({ error: message, code }) + "\n");
2377
+ }
2378
+ function parseParticipantSpec(spec) {
2379
+ if (!spec || typeof spec !== "string") {
2380
+ throw new Error(`--participant must be a non-empty string, got: ${JSON.stringify(spec)}`);
2381
+ }
2382
+ const parts = spec.split(":");
2383
+ if (parts.length < 3) {
2384
+ throw new Error(
2385
+ `--participant must be 'type:id:role' (e.g. agent:5:reviewer), got: ${JSON.stringify(spec)}`
2386
+ );
2387
+ }
2388
+ const [ptype, pid, role, ...rest] = parts;
2389
+ if (ptype !== "agent" && ptype !== "user") {
2390
+ throw new Error(`--participant type must be 'agent' or 'user', got: ${JSON.stringify(ptype)}`);
2391
+ }
2392
+ const value = Number.parseInt(pid, 10);
2393
+ if (!Number.isFinite(value) || Number.isNaN(value)) {
2394
+ throw new Error(`--participant id must be int, got: ${JSON.stringify(pid)}`);
2395
+ }
2396
+ const roleMap = { reviewer: ROLE_REVIEWER, qa: ROLE_QA };
2397
+ let roleId;
2398
+ if (role in roleMap) {
2399
+ roleId = roleMap[role];
2400
+ } else if (role === "role") {
2401
+ if (rest.length === 0) {
2402
+ throw new Error(`--participant 'role:NUMBER' needs a number, got: ${JSON.stringify(spec)}`);
2403
+ }
2404
+ const n = Number.parseInt(rest[0], 10);
2405
+ if (!Number.isFinite(n) || Number.isNaN(n)) {
2406
+ throw new Error(`--participant role id must be int, got: ${JSON.stringify(rest[0])}`);
2407
+ }
2408
+ roleId = n;
2409
+ } else {
2410
+ throw new Error(
2411
+ `--participant unknown role ${JSON.stringify(role)}; expected 'reviewer', 'qa', or 'role:NUMBER'`
2412
+ );
2413
+ }
2414
+ return { participable_id: value, type: ptype, role_id: roleId };
2415
+ }
2416
+ function parseArgs(argv) {
2417
+ const positional = [];
2418
+ const flags = {};
2419
+ for (let i = 0; i < argv.length; i++) {
2420
+ const a = argv[i];
2421
+ if (a.startsWith("--")) {
2422
+ const name = a.slice(2);
2423
+ const next = argv[i + 1];
2424
+ if (next === void 0 || next.startsWith("--")) {
2425
+ flags[name] = true;
2426
+ } else {
2427
+ if (flags[name] !== void 0) {
2428
+ const cur = flags[name];
2429
+ if (Array.isArray(cur)) cur.push(next);
2430
+ else flags[name] = [cur, next];
2431
+ } else {
2432
+ flags[name] = next;
2433
+ }
2434
+ i++;
2435
+ }
2436
+ } else {
2437
+ positional.push(a);
2438
+ }
2439
+ }
2440
+ return { positional, flags };
2441
+ }
2442
+ function flagStr(args, key) {
2443
+ const v = args.flags[key];
2444
+ if (v === void 0 || v === true) return void 0;
2445
+ if (Array.isArray(v)) return v[v.length - 1];
2446
+ return v;
2447
+ }
2448
+ function flagInt(args, key) {
2449
+ const v = flagStr(args, key);
2450
+ if (v === void 0) return void 0;
2451
+ const n = Number.parseInt(v, 10);
2452
+ if (Number.isNaN(n)) throw new Error(`--${key} must be int, got: ${JSON.stringify(v)}`);
2453
+ return n;
2454
+ }
2455
+ function flagList(args, key) {
2456
+ const v = args.flags[key];
2457
+ if (v === void 0) return [];
2458
+ if (v === true) return [];
2459
+ if (Array.isArray(v)) return v;
2460
+ return [v];
2461
+ }
2462
+ function requireFlag(args, key) {
2463
+ const v = flagStr(args, key);
2464
+ if (v === void 0) throw new Error(`--${key} is required`);
2465
+ return v;
2466
+ }
2467
+ function dump(value) {
2468
+ out(value);
2469
+ }
2470
+ var contractClientFactory = () => new ContractClient();
2471
+ var timelogClientFactory = () => new TimelogClient();
2472
+ async function cmdContract(argv) {
2473
+ const action = argv[0];
2474
+ if (!action) {
2475
+ err("contract: subcommand required", "args");
2476
+ return 1;
2477
+ }
2478
+ const rest = argv.slice(1);
2479
+ const args = parseArgs(rest);
2480
+ const client = contractClientFactory();
2481
+ try {
2482
+ switch (action) {
2483
+ case "list": {
2484
+ dump(await client.listContracts());
2485
+ return 0;
2486
+ }
2487
+ case "members": {
2488
+ const cid = Number.parseInt(args.positional[0] ?? "", 10);
2489
+ if (Number.isNaN(cid)) {
2490
+ err("contract members: cid required", "args");
2491
+ return 1;
2492
+ }
2493
+ dump(await client.members(cid));
2494
+ return 0;
2495
+ }
2496
+ case "tasks": {
2497
+ const explicit = args.positional[0];
2498
+ const ids = explicit !== void 0 ? [explicit] : contractIdsFromEnv();
2499
+ if (ids.length === 0) {
2500
+ err("no contract id (positional or CEKI_CONTRACT_IDS)", "args");
2501
+ return 1;
2502
+ }
2503
+ for (const cid of ids) {
2504
+ process.stdout.write(`--- contract ${cid} ---
2505
+ `);
2506
+ dump(await client.tasks(Number(cid)));
2507
+ }
2508
+ return 0;
2509
+ }
2510
+ case "my-jobs": {
2511
+ dump(await client.myJobs());
2512
+ return 0;
2513
+ }
2514
+ case "task": {
2515
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2516
+ if (Number.isNaN(eid)) {
2517
+ err("contract task: eid required", "args");
2518
+ return 1;
2519
+ }
2520
+ dump(await client.task(eid));
2521
+ return 0;
2522
+ }
2523
+ case "children": {
2524
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2525
+ if (Number.isNaN(eid)) {
2526
+ err("contract children: eid required", "args");
2527
+ return 1;
2528
+ }
2529
+ dump(await client.children(eid));
2530
+ return 0;
2531
+ }
2532
+ case "history": {
2533
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2534
+ if (Number.isNaN(eid)) {
2535
+ err("contract history: eid required", "args");
2536
+ return 1;
2537
+ }
2538
+ dump(await client.history(eid, { limit: flagInt(args, "limit") }));
2539
+ return 0;
2540
+ }
2541
+ case "create": {
2542
+ const explicit = args.positional[0];
2543
+ let cid;
2544
+ if (explicit !== void 0) {
2545
+ cid = Number.parseInt(explicit, 10);
2546
+ } else {
2547
+ const envIds = contractIdsFromEnv();
2548
+ if (envIds.length === 0) {
2549
+ err("contract id required (positional or CEKI_CONTRACT_IDS)", "args");
2550
+ return 1;
2551
+ }
2552
+ cid = Number.parseInt(envIds[0], 10);
2553
+ }
2554
+ if (Number.isNaN(cid)) {
2555
+ err("contract create: cid must be int", "args");
2556
+ return 1;
2557
+ }
2558
+ const label = requireFlag(args, "label");
2559
+ const dataRaw = flagStr(args, "data");
2560
+ const dataObj = dataRaw ? JSON.parse(dataRaw) : void 0;
2561
+ let extras = [];
2562
+ try {
2563
+ extras = flagList(args, "participant").map(parseParticipantSpec);
2564
+ } catch (e) {
2565
+ err(e.message, "args");
2566
+ return 1;
2567
+ }
2568
+ dump(
2569
+ await client.create(cid, {
2570
+ label,
2571
+ type: flagInt(args, "type"),
2572
+ status: flagInt(args, "status"),
2573
+ kalScheduleId: flagInt(args, "kal-schedule"),
2574
+ start: flagStr(args, "start"),
2575
+ end: flagStr(args, "end"),
2576
+ timezone: flagStr(args, "timezone"),
2577
+ date: flagStr(args, "date"),
2578
+ duration: flagInt(args, "duration"),
2579
+ amount: flagInt(args, "amount"),
2580
+ currency: flagStr(args, "currency"),
2581
+ description: flagStr(args, "desc"),
2582
+ data: dataObj,
2583
+ benefitable: flagStr(args, "benefitable"),
2584
+ reviewer: flagStr(args, "reviewer"),
2585
+ qa: flagStr(args, "qa"),
2586
+ participants: extras.length > 0 ? extras : void 0
2587
+ })
2588
+ );
2589
+ return 0;
2590
+ }
2591
+ case "comment": {
2592
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2593
+ if (Number.isNaN(eid)) {
2594
+ err("contract comment: eid required", "args");
2595
+ return 1;
2596
+ }
2597
+ dump(
2598
+ await client.comment(eid, {
2599
+ label: flagStr(args, "label"),
2600
+ type: flagInt(args, "type"),
2601
+ status: flagInt(args, "status"),
2602
+ start: flagStr(args, "start"),
2603
+ end: flagStr(args, "end"),
2604
+ date: flagStr(args, "date"),
2605
+ duration: flagInt(args, "duration"),
2606
+ amount: flagInt(args, "amount"),
2607
+ currency: flagStr(args, "currency"),
2608
+ description: flagStr(args, "desc"),
2609
+ benefitable: flagStr(args, "benefitable")
2610
+ })
2611
+ );
2612
+ return 0;
2613
+ }
2614
+ case "propose": {
2615
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2616
+ if (Number.isNaN(eid)) {
2617
+ err("contract propose: eid required", "args");
2618
+ return 1;
2619
+ }
2620
+ dump(
2621
+ await client.propose(eid, {
2622
+ status: flagInt(args, "status"),
2623
+ label: flagStr(args, "label"),
2624
+ description: flagStr(args, "desc"),
2625
+ start: flagStr(args, "start"),
2626
+ end: flagStr(args, "end"),
2627
+ date: flagStr(args, "date"),
2628
+ duration: flagInt(args, "duration"),
2629
+ amount: flagInt(args, "amount"),
2630
+ currency: flagStr(args, "currency"),
2631
+ benefitable: flagStr(args, "benefitable")
2632
+ })
2633
+ );
2634
+ return 0;
2635
+ }
2636
+ case "progress": {
2637
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2638
+ if (Number.isNaN(eid)) {
2639
+ err("contract progress: eid required", "args");
2640
+ return 1;
2641
+ }
2642
+ const desc = flagStr(args, "desc");
2643
+ if (desc === void 0) {
2644
+ err("contract progress: --desc is required", "args");
2645
+ return 1;
2646
+ }
2647
+ dump(
2648
+ await client.progress(eid, {
2649
+ status: flagInt(args, "status"),
2650
+ desc
2651
+ })
2652
+ );
2653
+ return 0;
2654
+ }
2655
+ case "vote": {
2656
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2657
+ if (Number.isNaN(eid)) {
2658
+ err("contract vote: eid required", "args");
2659
+ return 1;
2660
+ }
2661
+ const idsRaw = requireFlag(args, "ids");
2662
+ const voteRaw = requireFlag(args, "vote");
2663
+ const ids = idsRaw.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map((s) => Number.parseInt(s, 10));
2664
+ const vote = ["true", "1", "yes"].includes(voteRaw.toLowerCase());
2665
+ dump(await client.vote(eid, ids, vote));
2666
+ return 0;
2667
+ }
2668
+ case "poll": {
2669
+ const items = await client.poll();
2670
+ dump({ count: items.length, notifications: items });
2671
+ return 0;
2672
+ }
2673
+ case "watch": {
2674
+ const interval = Math.max(6, Number.parseInt(args.positional[0] ?? "8", 10) || 8);
2675
+ process.stderr.write(
2676
+ `[watch] poll every ${interval}s (limit 10/min/token; do not go below 6s)
2677
+ `
2678
+ );
2679
+ while (true) {
2680
+ const items = await client.poll();
2681
+ if (items.length > 0) {
2682
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
2683
+ for (const n of items) {
2684
+ process.stdout.write(JSON.stringify({ ts, notification: n }) + "\n");
2685
+ }
2686
+ }
2687
+ await new Promise((r) => setTimeout(r, interval * 1e3));
2688
+ }
2689
+ }
2690
+ case "tools": {
2691
+ dump(await client.tools());
2692
+ return 0;
2693
+ }
2694
+ case "raw": {
2695
+ const tool = args.positional[0];
2696
+ if (!tool) {
2697
+ err("contract raw: tool name required", "args");
2698
+ return 1;
2699
+ }
2700
+ const payloadRaw = args.positional[1] ?? "{}";
2701
+ const payload = JSON.parse(payloadRaw);
2702
+ dump(await client.raw(tool, payload));
2703
+ return 0;
2704
+ }
2705
+ default: {
2706
+ err(`unknown contract action: ${action}`, "args");
2707
+ return 1;
2708
+ }
2709
+ }
2710
+ } catch (e) {
2711
+ if (e instanceof ContractError) {
2712
+ err(e.message, "contract");
2713
+ return 1;
2714
+ }
2715
+ err(e.message ?? String(e), "error");
2716
+ return 1;
2717
+ }
2718
+ }
2719
+ async function cmdTimelog(argv) {
2720
+ const action = argv[0];
2721
+ if (!action) {
2722
+ err("timelog: subcommand required", "args");
2723
+ return 1;
2724
+ }
2725
+ const rest = argv.slice(1);
2726
+ const args = parseArgs(rest);
2727
+ const client = timelogClientFactory();
2728
+ try {
2729
+ switch (action) {
2730
+ case "start": {
2731
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2732
+ if (Number.isNaN(eid)) {
2733
+ err("timelog start: event_id required", "args");
2734
+ return 1;
2735
+ }
2736
+ dump(await client.start(eid));
2737
+ return 0;
2738
+ }
2739
+ case "stop": {
2740
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2741
+ if (Number.isNaN(eid)) {
2742
+ err("timelog stop: event_id required", "args");
2743
+ return 1;
2744
+ }
2745
+ dump(await client.stop(eid, flagStr(args, "label")));
2746
+ return 0;
2747
+ }
2748
+ case "check": {
2749
+ const eid = Number.parseInt(args.positional[0] ?? "", 10);
2750
+ if (Number.isNaN(eid)) {
2751
+ err("timelog check: event_id required", "args");
2752
+ return 1;
2753
+ }
2754
+ dump(await client.check(eid));
2755
+ return 0;
2756
+ }
2757
+ default: {
2758
+ err(`unknown timelog action: ${action}`, "args");
2759
+ return 1;
2760
+ }
2761
+ }
2762
+ } catch (e) {
2763
+ if (e instanceof ContractError) {
2764
+ err(e.message, "timelog");
2765
+ return 1;
2766
+ }
2767
+ err(e.message ?? String(e), "error");
2768
+ return 1;
2769
+ }
2770
+ }
2771
+
2772
+ // src/cli.ts
2773
+ function out2(data) {
1995
2774
  process.stdout.write(JSON.stringify(data) + "\n");
1996
2775
  }
1997
- function err(error, code = "error") {
2776
+ function err2(error, code = "error") {
1998
2777
  process.stderr.write(JSON.stringify({ error, code }) + "\n");
1999
2778
  }
2000
2779
  function getApiKey() {
2001
2780
  const key = process.env.CEKI_API_KEY;
2002
2781
  if (!key) {
2003
- err("CEKI_API_KEY not set", "auth");
2782
+ err2("CEKI_API_KEY not set", "auth");
2004
2783
  process.exit(2);
2005
2784
  }
2006
2785
  return key;
@@ -2032,14 +2811,14 @@ async function cmdRent(args) {
2032
2811
  if (args[i] === "--mode" && args[i + 1]) {
2033
2812
  const v = args[++i];
2034
2813
  if (v !== "incognito" && v !== "main") {
2035
- err("invalid mode, must be incognito or main", "args");
2814
+ err2("invalid mode, must be incognito or main", "args");
2036
2815
  process.exit(1);
2037
2816
  }
2038
2817
  mode = v;
2039
2818
  }
2040
2819
  }
2041
2820
  if (scheduleId == null) {
2042
- err("--schedule is required", "args");
2821
+ err2("--schedule is required", "args");
2043
2822
  process.exit(1);
2044
2823
  }
2045
2824
  const apiKey = getApiKey();
@@ -2057,7 +2836,7 @@ async function cmdRent(args) {
2057
2836
  schedule_id: browser.scheduleId,
2058
2837
  last_seen_ts: null
2059
2838
  });
2060
- out({
2839
+ out2({
2061
2840
  session_id: browser.sessionId,
2062
2841
  chat_topic_id: browser.chatTopicId,
2063
2842
  schedule_id: browser.scheduleId
@@ -2080,7 +2859,7 @@ async function cmdSearch(args) {
2080
2859
  const client = await connect(apiKey, connectOptions());
2081
2860
  try {
2082
2861
  const results = await client.search(filters, limit);
2083
- out(results);
2862
+ out2(results);
2084
2863
  } finally {
2085
2864
  await closeClient(client);
2086
2865
  }
@@ -2099,7 +2878,7 @@ async function cmdSessions(args) {
2099
2878
  try {
2100
2879
  const results = await client.listSessions({ active: !showAll, limit });
2101
2880
  if (jsonOutput) {
2102
- out(results);
2881
+ out2(results);
2103
2882
  } else {
2104
2883
  if (!results.length) {
2105
2884
  process.stdout.write("No sessions found.\n");
@@ -2128,7 +2907,7 @@ async function cmdMyBrowsers() {
2128
2907
  const client = await connect(apiKey, connectOptions());
2129
2908
  try {
2130
2909
  const results = await client.myBrowsers();
2131
- out(results);
2910
+ out2(results);
2132
2911
  } finally {
2133
2912
  await closeClient(client);
2134
2913
  }
@@ -2139,7 +2918,7 @@ async function cmdSnapshot(sid, args) {
2139
2918
  if ((args[i] === "-o" || args[i] === "--output") && args[i + 1]) outputPath = args[++i];
2140
2919
  }
2141
2920
  if (!outputPath) {
2142
- err("-o/--output is required", "args");
2921
+ err2("-o/--output is required", "args");
2143
2922
  process.exit(1);
2144
2923
  }
2145
2924
  const apiKey = getApiKey();
@@ -2158,7 +2937,7 @@ async function cmdSnapshot(sid, args) {
2158
2937
  text: m.text,
2159
2938
  ts: m.created_at
2160
2939
  }));
2161
- out({ screenshot: outputPath, chat: chatList, ts: snap.ts.toISOString() });
2940
+ out2({ screenshot: outputPath, chat: chatList, ts: snap.ts.toISOString() });
2162
2941
  } finally {
2163
2942
  await closeClient(client);
2164
2943
  }
@@ -2169,7 +2948,7 @@ function parseNoHuman(args) {
2169
2948
  async function cmdNavigate(sid, args) {
2170
2949
  const url = args.find((a) => !a.startsWith("--"));
2171
2950
  if (!url) {
2172
- err("URL is required", "args");
2951
+ err2("URL is required", "args");
2173
2952
  process.exit(1);
2174
2953
  }
2175
2954
  const raw = parseNoHuman(args);
@@ -2177,7 +2956,7 @@ async function cmdNavigate(sid, args) {
2177
2956
  const [client, browser] = await resumeBrowser(apiKey, sid);
2178
2957
  try {
2179
2958
  await browser.navigate(url, 3e4, raw ? { human: false } : void 0);
2180
- out({ ok: true });
2959
+ out2({ ok: true });
2181
2960
  } finally {
2182
2961
  await closeClient(client);
2183
2962
  }
@@ -2187,7 +2966,7 @@ async function cmdClick(sid, args) {
2187
2966
  const x = parseInt(positional[0], 10);
2188
2967
  const y = parseInt(positional[1], 10);
2189
2968
  if (isNaN(x) || isNaN(y)) {
2190
- err("x and y coordinates are required", "args");
2969
+ err2("x and y coordinates are required", "args");
2191
2970
  process.exit(1);
2192
2971
  }
2193
2972
  const raw = parseNoHuman(args);
@@ -2195,7 +2974,7 @@ async function cmdClick(sid, args) {
2195
2974
  const [client, browser] = await resumeBrowser(apiKey, sid);
2196
2975
  try {
2197
2976
  await browser.click(x, y, raw ? { human: false } : void 0);
2198
- out({ ok: true, pointer: [x, y] });
2977
+ out2({ ok: true, pointer: [x, y] });
2199
2978
  } finally {
2200
2979
  await closeClient(client);
2201
2980
  }
@@ -2209,7 +2988,7 @@ async function cmdType(sid, args) {
2209
2988
  } else if (!text) text = args[i];
2210
2989
  }
2211
2990
  if (!text) {
2212
- err("text is required", "args");
2991
+ err2("text is required", "args");
2213
2992
  process.exit(1);
2214
2993
  }
2215
2994
  const apiKey = getApiKey();
@@ -2217,7 +2996,7 @@ async function cmdType(sid, args) {
2217
2996
  try {
2218
2997
  const browser = await client.resume(sid);
2219
2998
  await browser.type(text, raw ? { human: false } : void 0);
2220
- out({ ok: true });
2999
+ out2({ ok: true });
2221
3000
  } finally {
2222
3001
  await closeClient(client);
2223
3002
  }
@@ -2228,7 +3007,7 @@ async function cmdScroll(sid, args) {
2228
3007
  const y = parseInt(positional[1], 10);
2229
3008
  const dy = parseInt(positional[2], 10);
2230
3009
  if (isNaN(x) || isNaN(y) || isNaN(dy)) {
2231
- err("x, y, dy are required", "args");
3010
+ err2("x, y, dy are required", "args");
2232
3011
  process.exit(1);
2233
3012
  }
2234
3013
  const raw = parseNoHuman(args);
@@ -2238,7 +3017,7 @@ async function cmdScroll(sid, args) {
2238
3017
  const scrollOpts = { x, y, deltaY: dy };
2239
3018
  if (raw) scrollOpts.human = false;
2240
3019
  await browser.scroll(scrollOpts);
2241
- out({ ok: true });
3020
+ out2({ ok: true });
2242
3021
  } finally {
2243
3022
  await closeClient(client);
2244
3023
  }
@@ -2251,11 +3030,11 @@ async function cmdChat(sid, action, args) {
2251
3030
  case "send": {
2252
3031
  const text = args[0];
2253
3032
  if (!text) {
2254
- err("text is required", "args");
3033
+ err2("text is required", "args");
2255
3034
  process.exit(1);
2256
3035
  }
2257
3036
  const result = await browser.chat.send(text);
2258
- out({ ok: true, message_id: result.messageId });
3037
+ out2({ ok: true, message_id: result.messageId });
2259
3038
  break;
2260
3039
  }
2261
3040
  case "send-image": {
@@ -2266,14 +3045,14 @@ async function cmdChat(sid, action, args) {
2266
3045
  if (args[i] === "--text" && args[i + 1]) text = args[++i];
2267
3046
  }
2268
3047
  if (!imagePath) {
2269
- err("--image is required", "args");
3048
+ err2("--image is required", "args");
2270
3049
  process.exit(1);
2271
3050
  }
2272
3051
  if (text) {
2273
3052
  await browser.chat.send(text);
2274
3053
  }
2275
3054
  const result = await browser.chat.sendImage(imagePath);
2276
- out({ ok: true, message_id: result.messageId });
3055
+ out2({ ok: true, message_id: result.messageId });
2277
3056
  break;
2278
3057
  }
2279
3058
  case "next": {
@@ -2286,7 +3065,7 @@ async function cmdChat(sid, action, args) {
2286
3065
  if (msgs.length > 0) {
2287
3066
  const m = msgs[0];
2288
3067
  updateLastSeenTs(sid, m.created_at);
2289
- out({ from: m.sender_id, text: m.text, ts: m.created_at });
3068
+ out2({ from: m.sender_id, text: m.text, ts: m.created_at });
2290
3069
  } else {
2291
3070
  let resolved = false;
2292
3071
  const waitPromise = new Promise((resolve2) => {
@@ -2307,9 +3086,9 @@ async function cmdChat(sid, action, args) {
2307
3086
  const msg = await waitPromise;
2308
3087
  if (msg) {
2309
3088
  updateLastSeenTs(sid, msg.created_at);
2310
- out({ from: msg.sender_id, text: msg.text, ts: msg.created_at });
3089
+ out2({ from: msg.sender_id, text: msg.text, ts: msg.created_at });
2311
3090
  } else {
2312
- out(null);
3091
+ out2(null);
2313
3092
  }
2314
3093
  }
2315
3094
  break;
@@ -2330,11 +3109,11 @@ async function cmdChat(sid, action, args) {
2330
3109
  if (args[i] === "--limit" && args[i + 1]) limit = parseInt(args[++i], 10);
2331
3110
  }
2332
3111
  const msgs = await browser.chat.history({ since, limit });
2333
- out(msgs.map((m) => ({ from: m.sender_id, text: m.text, ts: m.created_at })));
3112
+ out2(msgs.map((m) => ({ from: m.sender_id, text: m.text, ts: m.created_at })));
2334
3113
  break;
2335
3114
  }
2336
3115
  default:
2337
- err(`Unknown chat action: ${action}`, "args");
3116
+ err2(`Unknown chat action: ${action}`, "args");
2338
3117
  process.exit(1);
2339
3118
  }
2340
3119
  } finally {
@@ -2347,7 +3126,7 @@ async function cmdStop(sid) {
2347
3126
  try {
2348
3127
  await browser.close();
2349
3128
  deleteSession(sid);
2350
- out({ ok: true });
3129
+ out2({ ok: true });
2351
3130
  } finally {
2352
3131
  await closeClient(client);
2353
3132
  }
@@ -2367,7 +3146,7 @@ async function cmdProfile(sid, action, args) {
2367
3146
  if (args[i] === "--no-session-storage") noSessionStorage = true;
2368
3147
  }
2369
3148
  if (!outputPath) {
2370
- err("-o/--output is required", "args");
3149
+ err2("-o/--output is required", "args");
2371
3150
  process.exit(1);
2372
3151
  }
2373
3152
  const profile = await browser.profile.export({
@@ -2375,7 +3154,7 @@ async function cmdProfile(sid, action, args) {
2375
3154
  includeSessionStorage: !noSessionStorage
2376
3155
  });
2377
3156
  fs4.writeFileSync(outputPath, JSON.stringify(profile, null, 2), "utf-8");
2378
- out({ ok: true, path: outputPath });
3157
+ out2({ ok: true, path: outputPath });
2379
3158
  break;
2380
3159
  }
2381
3160
  case "import": {
@@ -2384,16 +3163,16 @@ async function cmdProfile(sid, action, args) {
2384
3163
  if ((args[i] === "-i" || args[i] === "--input") && args[i + 1]) inputPath = args[++i];
2385
3164
  }
2386
3165
  if (!inputPath) {
2387
- err("-i/--input is required", "args");
3166
+ err2("-i/--input is required", "args");
2388
3167
  process.exit(1);
2389
3168
  }
2390
3169
  const profileData = JSON.parse(fs4.readFileSync(inputPath, "utf-8"));
2391
3170
  await browser.profile.import(profileData);
2392
- out({ ok: true });
3171
+ out2({ ok: true });
2393
3172
  break;
2394
3173
  }
2395
3174
  default:
2396
- err(`Unknown profile action: ${action}`, "args");
3175
+ err2(`Unknown profile action: ${action}`, "args");
2397
3176
  process.exit(1);
2398
3177
  }
2399
3178
  } finally {
@@ -2405,7 +3184,7 @@ async function cmdWait(sid) {
2405
3184
  const [client, browser] = await resumeBrowser(apiKey, sid);
2406
3185
  try {
2407
3186
  const reason = await browser.waitUntilEnded();
2408
- out({ ended: true, reason });
3187
+ out2({ ended: true, reason });
2409
3188
  } finally {
2410
3189
  await closeClient(client);
2411
3190
  }
@@ -2418,7 +3197,7 @@ async function cmdScreenshot(sid, args) {
2418
3197
  if (args[i] === "--full") fullPage = true;
2419
3198
  }
2420
3199
  if (!outputPath) {
2421
- err("-o/--output is required", "args");
3200
+ err2("-o/--output is required", "args");
2422
3201
  process.exit(1);
2423
3202
  }
2424
3203
  const apiKey = getApiKey();
@@ -2426,7 +3205,7 @@ async function cmdScreenshot(sid, args) {
2426
3205
  try {
2427
3206
  const data = await browser.screenshot({ format: "png", fullPage });
2428
3207
  fs4.writeFileSync(outputPath, data);
2429
- out({ ok: true, path: outputPath });
3208
+ out2({ ok: true, path: outputPath });
2430
3209
  } finally {
2431
3210
  await closeClient(client);
2432
3211
  }
@@ -2436,7 +3215,7 @@ async function cmdSwitchTab(sid) {
2436
3215
  const [client, browser] = await resumeBrowser(apiKey, sid);
2437
3216
  try {
2438
3217
  await browser.switchTab();
2439
- out({ ok: true });
3218
+ out2({ ok: true });
2440
3219
  } finally {
2441
3220
  await closeClient(client);
2442
3221
  }
@@ -2451,7 +3230,7 @@ async function cmdConfigure(sid, args) {
2451
3230
  const [client, browser] = await resumeBrowser(apiKey, sid);
2452
3231
  try {
2453
3232
  await browser.configure(opts);
2454
- out({ ok: true });
3233
+ out2({ ok: true });
2455
3234
  } finally {
2456
3235
  await closeClient(client);
2457
3236
  }
@@ -2464,14 +3243,14 @@ async function cmdCdp(sid, args) {
2464
3243
  if (args[i] === "--params" && args[i + 1]) params = JSON.parse(args[++i]);
2465
3244
  }
2466
3245
  if (!method) {
2467
- err("--method is required", "args");
3246
+ err2("--method is required", "args");
2468
3247
  process.exit(1);
2469
3248
  }
2470
3249
  const apiKey = getApiKey();
2471
3250
  const [client, browser] = await resumeBrowser(apiKey, sid);
2472
3251
  try {
2473
3252
  const result = await browser.send({ method, params });
2474
- out(result);
3253
+ out2(result);
2475
3254
  } finally {
2476
3255
  await closeClient(client);
2477
3256
  }
@@ -2493,7 +3272,7 @@ async function cmdRequestCaptcha(sid, args) {
2493
3272
  completionTimeout: completion,
2494
3273
  autoAccept: !manual
2495
3274
  });
2496
- out({
3275
+ out2({
2497
3276
  solved: result.solved,
2498
3277
  proof_message_id: result.proofMessageId,
2499
3278
  cancel_reason: result.cancelReason,
@@ -2503,7 +3282,7 @@ async function cmdRequestCaptcha(sid, args) {
2503
3282
  if (!result.solved) process.exit(1);
2504
3283
  } catch (e) {
2505
3284
  if (e instanceof CaptchaTimeoutError) {
2506
- out({ solved: false, cancel_reason: `timeout:${e.phase}`, child_event_id: null, correction_id: null });
3285
+ out2({ solved: false, cancel_reason: `timeout:${e.phase}`, child_event_id: null, correction_id: null });
2507
3286
  process.exit(1);
2508
3287
  }
2509
3288
  throw e;
@@ -2523,18 +3302,18 @@ async function cmdUpload(sid, args) {
2523
3302
  if (args[i] === "--mime" && args[i + 1]) mime2 = args[++i];
2524
3303
  }
2525
3304
  if (!selector || !filePath) {
2526
- err("--selector and --file are required", "args");
3305
+ err2("--selector and --file are required", "args");
2527
3306
  process.exit(1);
2528
3307
  }
2529
3308
  if (!fs4.existsSync(filePath)) {
2530
- err(`File not found: ${filePath}`, "args");
3309
+ err2(`File not found: ${filePath}`, "args");
2531
3310
  process.exit(1);
2532
3311
  }
2533
3312
  const apiKey = getApiKey();
2534
3313
  const [client, browser] = await resumeBrowser(apiKey, sid);
2535
3314
  try {
2536
3315
  const result = await browser.upload(selector, filePath, filename, mime2);
2537
- out(result);
3316
+ out2(result);
2538
3317
  } finally {
2539
3318
  await closeClient(client);
2540
3319
  }
@@ -2568,12 +3347,46 @@ Commands:
2568
3347
  profile import <sid> -i file
2569
3348
  stop <sid>
2570
3349
 
3350
+ contract list
3351
+ contract members <cid>
3352
+ contract tasks [cid] (default: CEKI_CONTRACT_IDS)
3353
+ contract my-jobs
3354
+ contract task <eid>
3355
+ contract children <eid>
3356
+ contract history <eid> [--limit N]
3357
+ contract create [cid] --label X [--type N] [--status N] [--kal-schedule N]
3358
+ [--start S] [--end E] [--timezone TZ] [--date D]
3359
+ [--duration N] [--amount N] [--currency C]
3360
+ [--benefitable agent:N|user:N]
3361
+ [--reviewer agent:N|user:N] [--qa agent:N|user:N]
3362
+ [--participant agent:N:reviewer|user:N:qa|agent:N:role:NUM]...
3363
+ [--desc text] [--data JSON]
3364
+ contract comment <eid> [--label X] [--type N] [--status N] [--start S]
3365
+ [--end E] [--date D] [--duration N] [--amount N]
3366
+ [--currency C] [--benefitable agent:N] [--desc text]
3367
+ contract propose <eid> [--status N] [--label X] [--desc text] [--start S]
3368
+ [--end E] [--date D] [--duration N] [--amount N]
3369
+ [--currency C] [--benefitable agent:N]
3370
+ contract progress <eid> [--status N] --desc TEXT
3371
+ contract vote <eid> --ids 1,2,3 --vote true|false
3372
+ contract poll
3373
+ contract watch [interval]
3374
+ contract tools
3375
+ contract raw <tool> [JSON]
3376
+
3377
+ timelog start <event_id>
3378
+ timelog stop <event_id> [--label TEXT]
3379
+ timelog check <event_id>
3380
+
2571
3381
  Environment:
2572
3382
  CEKI_API_KEY (required)
2573
3383
  CEKI_RELAY_URL (default: wss://browser.ceki.me/ws/agent)
2574
3384
  CEKI_API_URL (default: https://api.ceki.me)
2575
3385
  CEKI_CHAT_URL (default: https://chat.ceki.me/api/chat)
2576
3386
  CEKI_BASIC_AUTH_USER / CEKI_BASIC_AUTH_PASS (optional)
3387
+ CEKI_AGENT_TOKEN (contract/timelog; falls back to CEKI_API_KEY)
3388
+ CEKI_AGENT_MCP_ENDPOINT (override /mcp/agent endpoint)
3389
+ CEKI_CONTRACT_IDS (default contract for 'contract tasks'/'create')
2577
3390
 
2578
3391
  Exit codes: 0=success, 1=error, 2=auth, 3=session_not_found, 4=timeout, 5=network`);
2579
3392
  }
@@ -2595,6 +3408,12 @@ async function main() {
2595
3408
  }
2596
3409
  const command = argv[0];
2597
3410
  const rest = argv.slice(1);
3411
+ if (command === "contract") {
3412
+ process.exit(await cmdContract(rest));
3413
+ }
3414
+ if (command === "timelog") {
3415
+ process.exit(await cmdTimelog(rest));
3416
+ }
2598
3417
  switch (command) {
2599
3418
  case "rent":
2600
3419
  await cmdRent(rest);
@@ -2649,45 +3468,45 @@ async function main() {
2649
3468
  break;
2650
3469
  case "chat":
2651
3470
  if (rest.length < 2) {
2652
- err("Usage: ceki chat <sid> <action> [args]", "args");
3471
+ err2("Usage: ceki chat <sid> <action> [args]", "args");
2653
3472
  process.exit(1);
2654
3473
  }
2655
3474
  await cmdChat(rest[0], rest[1], rest.slice(2));
2656
3475
  break;
2657
3476
  case "profile":
2658
3477
  if (rest.length < 2) {
2659
- err("Usage: ceki profile export|import <sid> [args]", "args");
3478
+ err2("Usage: ceki profile export|import <sid> [args]", "args");
2660
3479
  process.exit(1);
2661
3480
  }
2662
3481
  await cmdProfile(rest[1], rest[0], rest.slice(2));
2663
3482
  break;
2664
3483
  default:
2665
- err(`Unknown command: ${command}`, "args");
3484
+ err2(`Unknown command: ${command}`, "args");
2666
3485
  process.exit(1);
2667
3486
  }
2668
3487
  }
2669
3488
  main().catch((e) => {
2670
3489
  if (e instanceof SessionExpired || e instanceof SessionNotFound) {
2671
- err(String(e), "session_not_found");
3490
+ err2(String(e), "session_not_found");
2672
3491
  process.exit(3);
2673
3492
  }
2674
3493
  if (e instanceof NotOwner) {
2675
- err(String(e), "not_owner");
3494
+ err2(String(e), "not_owner");
2676
3495
  process.exit(3);
2677
3496
  }
2678
3497
  if (e instanceof TimeoutError) {
2679
- err(String(e), "timeout");
3498
+ err2(String(e), "timeout");
2680
3499
  process.exit(4);
2681
3500
  }
2682
3501
  if (e instanceof ConnectionLost || e instanceof AuthError || e instanceof TransportError) {
2683
- err(String(e), "network");
3502
+ err2(String(e), "network");
2684
3503
  process.exit(5);
2685
3504
  }
2686
3505
  if (e instanceof CekiBrowserError) {
2687
- err(String(e), "ceki_error");
3506
+ err2(String(e), "ceki_error");
2688
3507
  process.exit(1);
2689
3508
  }
2690
- err(e instanceof Error ? e.message : String(e), "error");
3509
+ err2(e instanceof Error ? e.message : String(e), "error");
2691
3510
  process.exit(1);
2692
3511
  });
2693
3512
  //# sourceMappingURL=cli.js.map