@wspc/cli 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +298 -93
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +39 -3
- package/dist/index.js +26 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/spec/openapi.json +2083 -562
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command63 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/generated/cli/invite/accept.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -970,6 +970,11 @@ var emailDelete = (options) => (options.client ?? client).post({
|
|
|
970
970
|
...options.headers
|
|
971
971
|
}
|
|
972
972
|
});
|
|
973
|
+
var emailDomainDelete = (options) => (options.client ?? client).delete({
|
|
974
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
975
|
+
url: "/email/domains/{domain}",
|
|
976
|
+
...options
|
|
977
|
+
});
|
|
973
978
|
var emailDomainGet = (options) => (options.client ?? client).get({
|
|
974
979
|
security: [{ scheme: "bearer", type: "http" }],
|
|
975
980
|
url: "/email/domains/{domain}",
|
|
@@ -1175,6 +1180,7 @@ var V1_CRED_KEYS = [
|
|
|
1175
1180
|
function migrateEnv(raw) {
|
|
1176
1181
|
const api_base = typeof raw.api_base === "string" ? raw.api_base : "";
|
|
1177
1182
|
const env = { api_base, accounts: {} };
|
|
1183
|
+
if (typeof raw.consistency_bookmark === "string") env.consistency_bookmark = raw.consistency_bookmark;
|
|
1178
1184
|
if (typeof raw.client_id === "string") env.client_id = raw.client_id;
|
|
1179
1185
|
if (raw.accounts && typeof raw.accounts === "object") {
|
|
1180
1186
|
env.accounts = raw.accounts;
|
|
@@ -1242,6 +1248,49 @@ var ConfigStore = class {
|
|
|
1242
1248
|
}
|
|
1243
1249
|
await fs.writeFile(this.configFile, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
1244
1250
|
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Read-modify-write the config under a cross-process file lock. The mutator
|
|
1253
|
+
* runs against a FRESH read taken inside the lock and edits it in place, so
|
|
1254
|
+
* two writers (e.g. token refresh and the consistency-bookmark writeback)
|
|
1255
|
+
* can't clobber each other from stale snapshots — the bug that revoked whole
|
|
1256
|
+
* refresh-token families when several CLI sessions ran concurrently.
|
|
1257
|
+
*/
|
|
1258
|
+
async update(mutate) {
|
|
1259
|
+
await this.withLock(async () => {
|
|
1260
|
+
const config = await this.read();
|
|
1261
|
+
mutate(config);
|
|
1262
|
+
await this.write(config);
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
async withLock(fn) {
|
|
1266
|
+
await fs.mkdir(this.configDir, { recursive: true, mode: 448 });
|
|
1267
|
+
const lockFile = this.configFile + ".lock";
|
|
1268
|
+
const STALE_MS = 1e4;
|
|
1269
|
+
const RETRY_MS = 25;
|
|
1270
|
+
const MAX_WAIT_MS = 5e3;
|
|
1271
|
+
let waited = 0;
|
|
1272
|
+
for (; ; ) {
|
|
1273
|
+
try {
|
|
1274
|
+
const fh = await fs.open(lockFile, "wx");
|
|
1275
|
+
await fh.close();
|
|
1276
|
+
break;
|
|
1277
|
+
} catch (e) {
|
|
1278
|
+
if (e.code !== "EEXIST") throw e;
|
|
1279
|
+
const age = await fs.stat(lockFile).then((s) => Date.now() - s.mtimeMs).catch(() => Infinity);
|
|
1280
|
+
if (age > STALE_MS || waited >= MAX_WAIT_MS) {
|
|
1281
|
+
await fs.rm(lockFile, { force: true });
|
|
1282
|
+
continue;
|
|
1283
|
+
}
|
|
1284
|
+
await new Promise((r) => setTimeout(r, RETRY_MS));
|
|
1285
|
+
waited += RETRY_MS;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
try {
|
|
1289
|
+
return await fn();
|
|
1290
|
+
} finally {
|
|
1291
|
+
await fs.rm(lockFile, { force: true });
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1245
1294
|
async currentEnv() {
|
|
1246
1295
|
const c = await this.read();
|
|
1247
1296
|
const name = c.current_env;
|
|
@@ -1252,10 +1301,79 @@ var ConfigStore = class {
|
|
|
1252
1301
|
}
|
|
1253
1302
|
};
|
|
1254
1303
|
|
|
1304
|
+
// src/handwritten/auth/consistency-fetch.ts
|
|
1305
|
+
var HEADER = "x-consistency-bookmark";
|
|
1306
|
+
var INVALID_BOOKMARK = "INVALID_CONSISTENCY_BOOKMARK";
|
|
1307
|
+
function normalizeBasePath(pathname) {
|
|
1308
|
+
const trimmed = pathname.replace(/\/+$/, "");
|
|
1309
|
+
return trimmed === "" ? "/" : trimmed;
|
|
1310
|
+
}
|
|
1311
|
+
function isUnderApiBase(url, apiBase) {
|
|
1312
|
+
const base = new URL(apiBase);
|
|
1313
|
+
const basePath = normalizeBasePath(base.pathname);
|
|
1314
|
+
return url.origin === base.origin && (basePath === "/" || url.pathname === basePath || url.pathname.startsWith(`${basePath}/`));
|
|
1315
|
+
}
|
|
1316
|
+
function isJsonContentType(contentType) {
|
|
1317
|
+
const mediaType = contentType.toLowerCase().split(";")[0]?.trim() ?? "";
|
|
1318
|
+
return mediaType === "application/json" || mediaType.endsWith("+json");
|
|
1319
|
+
}
|
|
1320
|
+
async function responseHasInvalidBookmark(response) {
|
|
1321
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
1322
|
+
if (!isJsonContentType(contentType)) return false;
|
|
1323
|
+
try {
|
|
1324
|
+
const body = await response.clone().json();
|
|
1325
|
+
return body.error?.code === INVALID_BOOKMARK;
|
|
1326
|
+
} catch {
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function createConsistencyFetch(opts) {
|
|
1331
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
1332
|
+
return async (input, init) => {
|
|
1333
|
+
const request = new Request(input, init);
|
|
1334
|
+
const url = new URL(request.url);
|
|
1335
|
+
const applies = isUnderApiBase(url, opts.apiBase);
|
|
1336
|
+
let outgoing = request;
|
|
1337
|
+
let injectedStoredBookmark = false;
|
|
1338
|
+
if (applies && !outgoing.headers.has(HEADER)) {
|
|
1339
|
+
const config = await opts.store.read();
|
|
1340
|
+
const bookmark = config.envs[opts.envName]?.consistency_bookmark;
|
|
1341
|
+
if (bookmark) {
|
|
1342
|
+
const headers = new Headers(outgoing.headers);
|
|
1343
|
+
headers.set(HEADER, bookmark);
|
|
1344
|
+
outgoing = new Request(outgoing, { headers });
|
|
1345
|
+
injectedStoredBookmark = true;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
if (!applies && outgoing.headers.has(HEADER)) {
|
|
1349
|
+
const headers = new Headers(outgoing.headers);
|
|
1350
|
+
headers.delete(HEADER);
|
|
1351
|
+
outgoing = new Request(outgoing, { headers });
|
|
1352
|
+
}
|
|
1353
|
+
const response = await fetchImpl(outgoing);
|
|
1354
|
+
if (!applies) return response;
|
|
1355
|
+
const nextBookmark = response.headers.get(HEADER);
|
|
1356
|
+
const shouldCheckInvalidBookmark = injectedStoredBookmark && !nextBookmark;
|
|
1357
|
+
const invalidBookmark = shouldCheckInvalidBookmark ? await responseHasInvalidBookmark(response) : false;
|
|
1358
|
+
const shouldClearBookmark = invalidBookmark;
|
|
1359
|
+
if (!nextBookmark && !shouldClearBookmark) return response;
|
|
1360
|
+
await opts.store.update((config) => {
|
|
1361
|
+
const env = config.envs[opts.envName];
|
|
1362
|
+
if (!env) return;
|
|
1363
|
+
if (nextBookmark) {
|
|
1364
|
+
env.consistency_bookmark = nextBookmark;
|
|
1365
|
+
} else if (shouldClearBookmark) {
|
|
1366
|
+
delete env.consistency_bookmark;
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
return response;
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1255
1373
|
// src/version.ts
|
|
1256
|
-
var VERSION = "0.0.
|
|
1257
|
-
var SPEC_SHA = "
|
|
1258
|
-
var SPEC_FETCHED_AT = "2026-06-
|
|
1374
|
+
var VERSION = "0.0.16";
|
|
1375
|
+
var SPEC_SHA = "b505a817";
|
|
1376
|
+
var SPEC_FETCHED_AT = "2026-06-16T07:42:43.911Z";
|
|
1259
1377
|
var API_BASE = "https://api.wspc.ai";
|
|
1260
1378
|
|
|
1261
1379
|
// src/index.ts
|
|
@@ -1268,17 +1386,30 @@ var WspcAuthExpiredError = class extends Error {
|
|
|
1268
1386
|
};
|
|
1269
1387
|
|
|
1270
1388
|
// src/handwritten/auth/sdk-auth.ts
|
|
1389
|
+
var USER_AGENT = `@wspc/cli/${VERSION}`;
|
|
1390
|
+
async function expiredMessage(res) {
|
|
1391
|
+
try {
|
|
1392
|
+
const body = await res.clone().json();
|
|
1393
|
+
if (!body.error) return void 0;
|
|
1394
|
+
const detail = body.error_description ? `: ${body.error_description}` : "";
|
|
1395
|
+
return `wspc token refresh failed (${body.error}${detail}); re-authenticate via \`wspc login\``;
|
|
1396
|
+
} catch {
|
|
1397
|
+
return void 0;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1271
1400
|
function createAuthInterceptor(mode) {
|
|
1272
1401
|
if ("apiKey" in mode) {
|
|
1273
1402
|
const apiKey = mode.apiKey;
|
|
1403
|
+
const fetchImpl2 = mode.fetchImpl ?? fetch;
|
|
1274
1404
|
return {
|
|
1275
1405
|
async onRequest(req) {
|
|
1276
1406
|
req.headers.set("authorization", `Bearer ${apiKey}`);
|
|
1407
|
+
req.headers.set("user-agent", USER_AGENT);
|
|
1277
1408
|
return req;
|
|
1278
1409
|
},
|
|
1279
1410
|
async execute(req) {
|
|
1280
1411
|
const out = await this.onRequest(req.clone());
|
|
1281
|
-
return
|
|
1412
|
+
return fetchImpl2(out);
|
|
1282
1413
|
}
|
|
1283
1414
|
};
|
|
1284
1415
|
}
|
|
@@ -1289,6 +1420,7 @@ function createAuthInterceptor(mode) {
|
|
|
1289
1420
|
return {
|
|
1290
1421
|
async onRequest(req) {
|
|
1291
1422
|
req.headers.set("authorization", `Bearer ${accessToken}`);
|
|
1423
|
+
req.headers.set("user-agent", USER_AGENT);
|
|
1292
1424
|
return req;
|
|
1293
1425
|
},
|
|
1294
1426
|
async execute(req) {
|
|
@@ -1296,7 +1428,10 @@ function createAuthInterceptor(mode) {
|
|
|
1296
1428
|
if (first.status !== 401) return first;
|
|
1297
1429
|
const refreshRes = await fetchImpl(`${mode.baseUrl}/auth/oauth/token`, {
|
|
1298
1430
|
method: "POST",
|
|
1299
|
-
headers: {
|
|
1431
|
+
headers: {
|
|
1432
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
1433
|
+
"user-agent": USER_AGENT
|
|
1434
|
+
},
|
|
1300
1435
|
body: new URLSearchParams({
|
|
1301
1436
|
grant_type: "refresh_token",
|
|
1302
1437
|
refresh_token: refreshToken,
|
|
@@ -1304,7 +1439,7 @@ function createAuthInterceptor(mode) {
|
|
|
1304
1439
|
})
|
|
1305
1440
|
});
|
|
1306
1441
|
if (!refreshRes.ok) {
|
|
1307
|
-
throw new WspcAuthExpiredError();
|
|
1442
|
+
throw new WspcAuthExpiredError(await expiredMessage(refreshRes));
|
|
1308
1443
|
}
|
|
1309
1444
|
const tokens = await refreshRes.json();
|
|
1310
1445
|
accessToken = tokens.access_token;
|
|
@@ -1357,10 +1492,10 @@ function resolveAccount(config, opts = {}) {
|
|
|
1357
1492
|
}
|
|
1358
1493
|
|
|
1359
1494
|
// src/handwritten/auth/load-sdk-client.ts
|
|
1360
|
-
function buildInterceptor(store, resolved) {
|
|
1495
|
+
function buildInterceptor(store, resolved, fetchImpl) {
|
|
1361
1496
|
const { envName, apiBase, clientId, email, creds } = resolved;
|
|
1362
1497
|
if (creds.api_key) {
|
|
1363
|
-
return createAuthInterceptor({ apiKey: creds.api_key });
|
|
1498
|
+
return createAuthInterceptor({ apiKey: creds.api_key, fetchImpl });
|
|
1364
1499
|
}
|
|
1365
1500
|
if (!clientId) {
|
|
1366
1501
|
throw new Error(
|
|
@@ -1372,14 +1507,15 @@ function buildInterceptor(store, resolved) {
|
|
|
1372
1507
|
refreshToken: creds.refresh_token,
|
|
1373
1508
|
baseUrl: apiBase,
|
|
1374
1509
|
clientId,
|
|
1510
|
+
fetchImpl,
|
|
1375
1511
|
onTokenRefresh: async ({ accessToken, refreshToken, expiresAt }) => {
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1512
|
+
await store.update((cfg) => {
|
|
1513
|
+
const a = cfg.envs[envName]?.accounts?.[email];
|
|
1514
|
+
if (!a) return;
|
|
1515
|
+
a.access_token = accessToken;
|
|
1516
|
+
a.refresh_token = refreshToken;
|
|
1517
|
+
a.access_token_expires_at = expiresAt;
|
|
1518
|
+
});
|
|
1383
1519
|
}
|
|
1384
1520
|
});
|
|
1385
1521
|
}
|
|
@@ -1387,7 +1523,13 @@ async function loadSdkClient(opts = {}) {
|
|
|
1387
1523
|
const store = opts.store ?? new ConfigStore();
|
|
1388
1524
|
const config = await store.read();
|
|
1389
1525
|
const resolved = resolveAccount(config, { accountOverride: process.env.WSPC_ACCOUNT });
|
|
1390
|
-
const
|
|
1526
|
+
const consistencyFetch = createConsistencyFetch({
|
|
1527
|
+
store,
|
|
1528
|
+
envName: resolved.envName,
|
|
1529
|
+
apiBase: resolved.apiBase,
|
|
1530
|
+
fetchImpl: opts.fetchImpl
|
|
1531
|
+
});
|
|
1532
|
+
const interceptor = buildInterceptor(store, resolved, consistencyFetch);
|
|
1391
1533
|
const rawClient = createClient(
|
|
1392
1534
|
createConfig({
|
|
1393
1535
|
baseUrl: resolved.apiBase,
|
|
@@ -1400,7 +1542,13 @@ async function loadAuthedFetch(opts = {}) {
|
|
|
1400
1542
|
const store = opts.store ?? new ConfigStore();
|
|
1401
1543
|
const config = await store.read();
|
|
1402
1544
|
const resolved = resolveAccount(config, { accountOverride: process.env.WSPC_ACCOUNT });
|
|
1403
|
-
const
|
|
1545
|
+
const consistencyFetch = createConsistencyFetch({
|
|
1546
|
+
store,
|
|
1547
|
+
envName: resolved.envName,
|
|
1548
|
+
apiBase: resolved.apiBase,
|
|
1549
|
+
fetchImpl: opts.fetchImpl
|
|
1550
|
+
});
|
|
1551
|
+
const interceptor = buildInterceptor(store, resolved, consistencyFetch);
|
|
1404
1552
|
const authedFetch = (input, init) => interceptor.execute(new Request(input, init));
|
|
1405
1553
|
return { fetch: authedFetch, baseUrl: resolved.apiBase };
|
|
1406
1554
|
}
|
|
@@ -2416,7 +2564,7 @@ var eventGetCommand = new Command19("show").description("Get a calendar event by
|
|
|
2416
2564
|
|
|
2417
2565
|
// src/generated/cli/event/set.ts
|
|
2418
2566
|
import { Command as Command20 } from "commander";
|
|
2419
|
-
var eventUpdateCommand = new Command20("set").description("Update a calendar event").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail the call if someone else has mutated the event since you last read. On mismatch the server returns 409 `VERSION_CONFLICT`
|
|
2567
|
+
var eventUpdateCommand = new Command20("set").description("Update a calendar event").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail the call if someone else has mutated the event since you last read. On mismatch the server returns 409 `VERSION_CONFLICT` and includes the current and sent versions in the message.").option("--title <value>", "New event title. Omit to leave unchanged.").option("--description <value>", "New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass an empty string to clear; omit to leave unchanged.").option("--start <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("--end <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("-l, --location <value>", "New location. Pass an empty string to clear; omit to leave unchanged.").option("-u, --url <value>", "New meeting link. Pass an empty string to clear; omit to leave unchanged.").option("--status <value>", "Lifecycle status. `confirmed`: the event will happen (default). `tentative`: organizer has not finalized; still visible in lists. `cancelled`: the event was called off but the record is kept so attendees can be notified and history audited; distinct from soft-delete (DELETE `/calendar/events/{id}`) which hides the event from default list responses.").option("--attendee <value>", "If provided, REPLACES the attendee list (after case-insensitive email dedupe, up to 50). Added attendees receive a fresh invitation, kept attendees receive an update email, removed attendees receive a cancellation.", (val, memo) => {
|
|
2420
2568
|
memo.push(val);
|
|
2421
2569
|
return memo;
|
|
2422
2570
|
}, []).option("--all-day", "all_day").option("--tz <zone>", "IANA timezone for relative time parsing").action(async (id, opts) => {
|
|
@@ -2614,9 +2762,30 @@ var emailDeleteCommand = new Command27("rm").description("Soft-delete inbound em
|
|
|
2614
2762
|
render({ kind: "email_delete", display: { "shape": "object", "format": {} } }, result.data);
|
|
2615
2763
|
});
|
|
2616
2764
|
|
|
2617
|
-
// src/generated/cli/domain/
|
|
2765
|
+
// src/generated/cli/domain/rm.ts
|
|
2618
2766
|
import { Command as Command28 } from "commander";
|
|
2619
|
-
var
|
|
2767
|
+
var emailDomainDeleteCommand = new Command28("rm").description("Delete a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
|
|
2768
|
+
const client2 = await loadSdkClient();
|
|
2769
|
+
const result = await emailDomainDelete({
|
|
2770
|
+
client: client2._rawClient,
|
|
2771
|
+
path: {
|
|
2772
|
+
domain
|
|
2773
|
+
}
|
|
2774
|
+
});
|
|
2775
|
+
if (result.error || !result.response?.ok) {
|
|
2776
|
+
process.stderr.write(
|
|
2777
|
+
`HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
|
|
2778
|
+
`
|
|
2779
|
+
);
|
|
2780
|
+
process.exitCode = 1;
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
render({ kind: "email_domain_delete", display: void 0 }, result.data);
|
|
2784
|
+
});
|
|
2785
|
+
|
|
2786
|
+
// src/generated/cli/domain/show.ts
|
|
2787
|
+
import { Command as Command29 } from "commander";
|
|
2788
|
+
var emailDomainGetCommand = new Command29("show").description("Get one cached custom domain").argument("<domain>", "domain").action(async (domain, opts) => {
|
|
2620
2789
|
const client2 = await loadSdkClient();
|
|
2621
2790
|
const result = await emailDomainGet({
|
|
2622
2791
|
client: client2._rawClient,
|
|
@@ -2636,8 +2805,8 @@ var emailDomainGetCommand = new Command28("show").description("Get one cached cu
|
|
|
2636
2805
|
});
|
|
2637
2806
|
|
|
2638
2807
|
// src/generated/cli/email/show.ts
|
|
2639
|
-
import { Command as
|
|
2640
|
-
var emailGetCommand = new
|
|
2808
|
+
import { Command as Command30 } from "commander";
|
|
2809
|
+
var emailGetCommand = new Command30("show").description("Get an inbound email by id").argument("<id>", "id").option("--include-html <value>", "When `true`, fetch the HTML body from R2 and include it as `html_body` in the response. Costs an extra R2 read; omit if you only need text.").option("--include-deleted <value>", "When `true`, allow fetching a soft-deleted email. Defaults to `false` (returns 404 for soft-deleted rows).").action(async (id, opts) => {
|
|
2641
2810
|
const client2 = await loadSdkClient();
|
|
2642
2811
|
const result = await emailGet({
|
|
2643
2812
|
client: client2._rawClient,
|
|
@@ -2661,8 +2830,8 @@ var emailGetCommand = new Command29("show").description("Get an inbound email by
|
|
|
2661
2830
|
});
|
|
2662
2831
|
|
|
2663
2832
|
// src/generated/cli/email/ls.ts
|
|
2664
|
-
import { Command as
|
|
2665
|
-
var emailListCommand = new
|
|
2833
|
+
import { Command as Command31 } from "commander";
|
|
2834
|
+
var emailListCommand = new Command31("ls").description("List inbound emails").option("--limit <value>", "Max items to return (clamped to 1-100). Defaults to 20 server-side.").option("--alias-email <value>", "If set, only return emails received on this full alias email address.").option("--unread-only <value>", "When `true`, only return emails with `is_read=false`.").option("--since <value>", "Unix epoch milliseconds \u2014 only return emails with `received_at >= since`. Useful for incremental sync.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").option("--include-deleted <value>", "When `true`, also return soft-deleted emails. Defaults to `false`.").action(async (opts) => {
|
|
2666
2835
|
const client2 = await loadSdkClient();
|
|
2667
2836
|
const result = await emailList({
|
|
2668
2837
|
client: client2._rawClient,
|
|
@@ -2687,8 +2856,8 @@ var emailListCommand = new Command30("ls").description("List inbound emails").op
|
|
|
2687
2856
|
});
|
|
2688
2857
|
|
|
2689
2858
|
// src/generated/cli/email/read.ts
|
|
2690
|
-
import { Command as
|
|
2691
|
-
var emailMarkReadCommand = new
|
|
2859
|
+
import { Command as Command32 } from "commander";
|
|
2860
|
+
var emailMarkReadCommand = new Command32("read").description("Mark inbound emails as read").argument("<id...>", "id").action(async (id, opts) => {
|
|
2692
2861
|
const idRaw = id;
|
|
2693
2862
|
const ids = idRaw.length > 0 ? idRaw : void 0;
|
|
2694
2863
|
const client2 = await loadSdkClient();
|
|
@@ -2710,8 +2879,8 @@ var emailMarkReadCommand = new Command31("read").description("Mark inbound email
|
|
|
2710
2879
|
});
|
|
2711
2880
|
|
|
2712
2881
|
// src/generated/cli/email/unread.ts
|
|
2713
|
-
import { Command as
|
|
2714
|
-
var emailMarkUnreadCommand = new
|
|
2882
|
+
import { Command as Command33 } from "commander";
|
|
2883
|
+
var emailMarkUnreadCommand = new Command33("unread").description("Mark inbound emails as unread").argument("<id...>", "id").action(async (id, opts) => {
|
|
2715
2884
|
const idRaw = id;
|
|
2716
2885
|
const ids = idRaw.length > 0 ? idRaw : void 0;
|
|
2717
2886
|
const client2 = await loadSdkClient();
|
|
@@ -2733,8 +2902,8 @@ var emailMarkUnreadCommand = new Command32("unread").description("Mark inbound e
|
|
|
2733
2902
|
});
|
|
2734
2903
|
|
|
2735
2904
|
// src/generated/cli/domain/verify.ts
|
|
2736
|
-
import { Command as
|
|
2737
|
-
var emailDomainVerifyCommand = new
|
|
2905
|
+
import { Command as Command34 } from "commander";
|
|
2906
|
+
var emailDomainVerifyCommand = new Command34("verify").description("Verify a custom domain with the provider").argument("<domain>", "domain").action(async (domain, opts) => {
|
|
2738
2907
|
const client2 = await loadSdkClient();
|
|
2739
2908
|
const result = await emailDomainVerify({
|
|
2740
2909
|
client: client2._rawClient,
|
|
@@ -2754,8 +2923,8 @@ var emailDomainVerifyCommand = new Command33("verify").description("Verify a cus
|
|
|
2754
2923
|
});
|
|
2755
2924
|
|
|
2756
2925
|
// src/generated/cli/push/config/rm.ts
|
|
2757
|
-
import { Command as
|
|
2758
|
-
var pushConfigDeleteCommand = new
|
|
2926
|
+
import { Command as Command35 } from "commander";
|
|
2927
|
+
var pushConfigDeleteCommand = new Command35("rm").description("Remove a push transport").argument("<transport>", "transport").action(async (transport, opts) => {
|
|
2759
2928
|
const client2 = await loadSdkClient();
|
|
2760
2929
|
const result = await pushConfigDelete({
|
|
2761
2930
|
client: client2._rawClient,
|
|
@@ -2775,8 +2944,8 @@ var pushConfigDeleteCommand = new Command34("rm").description("Remove a push tra
|
|
|
2775
2944
|
});
|
|
2776
2945
|
|
|
2777
2946
|
// src/generated/cli/push/config/set.ts
|
|
2778
|
-
import { Command as
|
|
2779
|
-
var pushConfigSetCommand = new
|
|
2947
|
+
import { Command as Command36 } from "commander";
|
|
2948
|
+
var pushConfigSetCommand = new Command36("set").description("Register or update a push transport").option("--transport <value>", "Transport discriminator. `telegram` is the only supported value today \u2014 push delivers via a Telegram bot DM. Future transports (web push, iOS/Android, generic webhook) will be added as additional discriminator values.").option("--target-bot-username <value>", "Telegram bot username (with leading `@`, 5\u201332 alphanumeric/underscore characters). This is the bot the user has already started a chat with \u2014 wspc DMs notifications to it via the Telegram Bot API.").action(async (opts) => {
|
|
2780
2949
|
const client2 = await loadSdkClient();
|
|
2781
2950
|
const result = await pushConfigSet({
|
|
2782
2951
|
client: client2._rawClient,
|
|
@@ -2799,8 +2968,8 @@ var pushConfigSetCommand = new Command35("set").description("Register or update
|
|
|
2799
2968
|
});
|
|
2800
2969
|
|
|
2801
2970
|
// src/generated/cli/push/config/show.ts
|
|
2802
|
-
import { Command as
|
|
2803
|
-
var pushConfigGetCommand = new
|
|
2971
|
+
import { Command as Command37 } from "commander";
|
|
2972
|
+
var pushConfigGetCommand = new Command37("show").description("List the caller's push transports").action(async (opts) => {
|
|
2804
2973
|
const client2 = await loadSdkClient();
|
|
2805
2974
|
const result = await pushConfigGet({
|
|
2806
2975
|
client: client2._rawClient
|
|
@@ -2817,8 +2986,8 @@ var pushConfigGetCommand = new Command36("show").description("List the caller's
|
|
|
2817
2986
|
});
|
|
2818
2987
|
|
|
2819
2988
|
// src/generated/cli/push/test.ts
|
|
2820
|
-
import { Command as
|
|
2821
|
-
var pushTestCommand = new
|
|
2989
|
+
import { Command as Command38 } from "commander";
|
|
2990
|
+
var pushTestCommand = new Command38("test").description("Send a test push notification").option("--transport <value>", "Which transport to send the test message through. Must match a transport the caller has already registered via `POST /push/config`; today only `telegram` is supported.").action(async (opts) => {
|
|
2822
2991
|
const client2 = await loadSdkClient();
|
|
2823
2992
|
const result = await pushTest({
|
|
2824
2993
|
client: client2._rawClient,
|
|
@@ -2841,8 +3010,8 @@ var pushTestCommand = new Command37("test").description("Send a test push notifi
|
|
|
2841
3010
|
});
|
|
2842
3011
|
|
|
2843
3012
|
// src/generated/cli/todo/comment/add.ts
|
|
2844
|
-
import { Command as
|
|
2845
|
-
var todoCommentCreateCommand = new
|
|
3013
|
+
import { Command as Command39 } from "commander";
|
|
3014
|
+
var todoCommentCreateCommand = new Command39("add").description("Add a comment to a todo").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
|
|
2846
3015
|
const client2 = await loadSdkClient();
|
|
2847
3016
|
const result = await todoCommentCreate({
|
|
2848
3017
|
client: client2._rawClient,
|
|
@@ -2865,8 +3034,8 @@ var todoCommentCreateCommand = new Command38("add").description("Add a comment t
|
|
|
2865
3034
|
});
|
|
2866
3035
|
|
|
2867
3036
|
// src/generated/cli/todo/comment/ls.ts
|
|
2868
|
-
import { Command as
|
|
2869
|
-
var todoCommentListCommand = new
|
|
3037
|
+
import { Command as Command40 } from "commander";
|
|
3038
|
+
var todoCommentListCommand = new Command40("ls").description("List comments on a todo").argument("<id>", "id").option("--order <value>", "order").option("--include-deleted <value>", "include_deleted").option("--limit <value>", "Max comments to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (id, opts) => {
|
|
2870
3039
|
const client2 = await loadSdkClient();
|
|
2871
3040
|
const result = await todoCommentList({
|
|
2872
3041
|
client: client2._rawClient,
|
|
@@ -2892,8 +3061,8 @@ var todoCommentListCommand = new Command39("ls").description("List comments on a
|
|
|
2892
3061
|
});
|
|
2893
3062
|
|
|
2894
3063
|
// src/generated/cli/todo/project/add.ts
|
|
2895
|
-
import { Command as
|
|
2896
|
-
var projectCreateCommand = new
|
|
3064
|
+
import { Command as Command41 } from "commander";
|
|
3065
|
+
var projectCreateCommand = new Command41("add").description("Create a project").argument("<name>", "name").option("--default-todo-type-id <value>", "default_todo_type_id").action(async (name, opts) => {
|
|
2897
3066
|
const client2 = await loadSdkClient();
|
|
2898
3067
|
const result = await projectCreate({
|
|
2899
3068
|
client: client2._rawClient,
|
|
@@ -2914,8 +3083,8 @@ var projectCreateCommand = new Command40("add").description("Create a project").
|
|
|
2914
3083
|
});
|
|
2915
3084
|
|
|
2916
3085
|
// src/generated/cli/todo/project/ls.ts
|
|
2917
|
-
import { Command as
|
|
2918
|
-
var projectListCommand = new
|
|
3086
|
+
import { Command as Command42 } from "commander";
|
|
3087
|
+
var projectListCommand = new Command42("ls").description("List projects").option("--include-deleted <value>", "Set to `true` to include soft-deleted projects in the response.").action(async (opts) => {
|
|
2919
3088
|
const client2 = await loadSdkClient();
|
|
2920
3089
|
const result = await projectList({
|
|
2921
3090
|
client: client2._rawClient,
|
|
@@ -2935,8 +3104,8 @@ var projectListCommand = new Command41("ls").description("List projects").option
|
|
|
2935
3104
|
});
|
|
2936
3105
|
|
|
2937
3106
|
// src/generated/cli/todo/rule/add.ts
|
|
2938
|
-
import { Command as
|
|
2939
|
-
var recurrenceRuleCreateCommand = new
|
|
3107
|
+
import { Command as Command43 } from "commander";
|
|
3108
|
+
var recurrenceRuleCreateCommand = new Command43("add").description("Create a recurring todo rule").argument("<title>", "title").option("--rrule <value>", "rrule").option("--dtstart <value>", "dtstart").option("--description <value>", "description").option("--parent-id <value>", "parent_id").option("-p, --project <value>", "Project for the recurrence rule, its template todo, and all materialized instances. Must be an active project in the caller's organization.").option("-t, --type <value>", "type_id").action(async (title, opts) => {
|
|
2940
3109
|
const client2 = await loadSdkClient();
|
|
2941
3110
|
const result = await recurrenceRuleCreate({
|
|
2942
3111
|
client: client2._rawClient,
|
|
@@ -2962,8 +3131,8 @@ var recurrenceRuleCreateCommand = new Command42("add").description("Create a rec
|
|
|
2962
3131
|
});
|
|
2963
3132
|
|
|
2964
3133
|
// src/generated/cli/todo/rule/ls.ts
|
|
2965
|
-
import { Command as
|
|
2966
|
-
var recurrenceRuleListCommand = new
|
|
3134
|
+
import { Command as Command44 } from "commander";
|
|
3135
|
+
var recurrenceRuleListCommand = new Command44("ls").description("List recurring todo rules").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").action(async (opts) => {
|
|
2967
3136
|
const client2 = await loadSdkClient();
|
|
2968
3137
|
const result = await recurrenceRuleList({
|
|
2969
3138
|
client: client2._rawClient,
|
|
@@ -2984,7 +3153,7 @@ var recurrenceRuleListCommand = new Command43("ls").description("List recurring
|
|
|
2984
3153
|
});
|
|
2985
3154
|
|
|
2986
3155
|
// src/generated/cli/todo/add.ts
|
|
2987
|
-
import { Command as
|
|
3156
|
+
import { Command as Command45 } from "commander";
|
|
2988
3157
|
|
|
2989
3158
|
// src/handwritten/utils/parse-json-field.ts
|
|
2990
3159
|
function parseJsonField(raw, flag) {
|
|
@@ -2999,7 +3168,7 @@ function parseJsonField(raw, flag) {
|
|
|
2999
3168
|
}
|
|
3000
3169
|
|
|
3001
3170
|
// src/generated/cli/todo/add.ts
|
|
3002
|
-
var todoCreateCommand = new
|
|
3171
|
+
var todoCreateCommand = new Command45("add").description("Create a todo").argument("<title>", "title").option("-p, --project <value>", "Project id to assign this todo to. It must be an active project in the caller's organization.").option("--description <value>", "Free-form details about the todo. Fully supports GFM Markdown (tables, strikethrough, task lists). Stored verbatim; client applications are responsible for rendering. Optional. Passing `null` is strictly rejected.").option("--parent-id <value>", "Parent todo ID (`tod_<ULID>`) to attach this todo as a child under another todo. Omit or pass `null` to create a root-level todo. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`. To make a subtask appear on every occurrence of a recurring rule, set this to that rule's template todo id (the template id returned when the rule is created); the server re-materializes future occurrences so each carries the subtask.").option("--status <value>", "Initial status of the todo. Omit to default to `open`. Allowed values: `open`, `in_progress`, `done`, `cancelled`.").option("--due-at <value>", 'Optional calendar due date in ISO date-only format (`YYYY-MM-DD`). Stored without timezone offsets to represent the same local calendar day globally. Pass `""` or omit the field to skip setting a due date. Passing `null` is strictly rejected.').option("--type-id <value>", "Type id this todo belongs to. Omit to use the project's default type. When project_id is also supplied, the type must belong to the same project. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "Custom field values keyed by the field's immutable `key` (not the human `label`). Each value must match the declared field type: string fields require string values, and string_array fields require string arrays. Providing a key that is not declared on the resolved todo type is strictly rejected with `UNDECLARED_FIELD`. Missing required fields that lack a default value are rejected with `FIELD_REQUIRED`. Defaults declared on the type are auto-applied at create time.").action(async (title, opts) => {
|
|
3003
3172
|
const client2 = await loadSdkClient();
|
|
3004
3173
|
const result = await todoCreate({
|
|
3005
3174
|
client: client2._rawClient,
|
|
@@ -3026,8 +3195,8 @@ var todoCreateCommand = new Command44("add").description("Create a todo").argume
|
|
|
3026
3195
|
});
|
|
3027
3196
|
|
|
3028
3197
|
// src/generated/cli/todo/ls.ts
|
|
3029
|
-
import { Command as
|
|
3030
|
-
var todoListCommand = new
|
|
3198
|
+
import { Command as Command46 } from "commander";
|
|
3199
|
+
var todoListCommand = new Command46("ls").description("List todos with filters").option("-p, --project <value>", "Filter by project. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--parent-id <value>", "parent_id").option("-s, --status <value>", "status").option("--include-deleted <value>", "include_deleted").option("--include-templates <value>", "include_templates").option("--due-after <value>", "due_after").option("--due-before <value>", "due_before").option("--type-id <value>", "type_id").option("--sort-by <value>", "sort_by").option("--order <value>", "order").option("--include-orphan-fields <value>", "include_orphan_fields").option("--limit <value>", "Max todos to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (opts) => {
|
|
3031
3200
|
const client2 = await loadSdkClient();
|
|
3032
3201
|
const result = await todoList({
|
|
3033
3202
|
client: client2._rawClient,
|
|
@@ -3060,8 +3229,8 @@ var todoListCommand = new Command45("ls").description("List todos with filters")
|
|
|
3060
3229
|
});
|
|
3061
3230
|
|
|
3062
3231
|
// src/generated/cli/todo/type/ls.ts
|
|
3063
|
-
import { Command as
|
|
3064
|
-
var todoTypeListCommand = new
|
|
3232
|
+
import { Command as Command47 } from "commander";
|
|
3233
|
+
var todoTypeListCommand = new Command47("ls").description("List todo types").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
|
|
3065
3234
|
const client2 = await loadSdkClient();
|
|
3066
3235
|
const result = await todoTypeList({
|
|
3067
3236
|
client: client2._rawClient,
|
|
@@ -3083,8 +3252,8 @@ var todoTypeListCommand = new Command46("ls").description("List todo types").opt
|
|
|
3083
3252
|
});
|
|
3084
3253
|
|
|
3085
3254
|
// src/generated/cli/todo/comment/rm.ts
|
|
3086
|
-
import { Command as
|
|
3087
|
-
var todoCommentDeleteCommand = new
|
|
3255
|
+
import { Command as Command48 } from "commander";
|
|
3256
|
+
var todoCommentDeleteCommand = new Command48("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
|
|
3088
3257
|
const client2 = await loadSdkClient();
|
|
3089
3258
|
const result = await todoCommentDelete({
|
|
3090
3259
|
client: client2._rawClient,
|
|
@@ -3104,8 +3273,8 @@ var todoCommentDeleteCommand = new Command47("rm").description("Soft-delete a co
|
|
|
3104
3273
|
});
|
|
3105
3274
|
|
|
3106
3275
|
// src/generated/cli/todo/comment/edit.ts
|
|
3107
|
-
import { Command as
|
|
3108
|
-
var todoCommentUpdateCommand = new
|
|
3276
|
+
import { Command as Command49 } from "commander";
|
|
3277
|
+
var todoCommentUpdateCommand = new Command49("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
|
|
3109
3278
|
const client2 = await loadSdkClient();
|
|
3110
3279
|
const result = await todoCommentUpdate({
|
|
3111
3280
|
client: client2._rawClient,
|
|
@@ -3128,8 +3297,8 @@ var todoCommentUpdateCommand = new Command48("edit").description("Edit a comment
|
|
|
3128
3297
|
});
|
|
3129
3298
|
|
|
3130
3299
|
// src/generated/cli/todo/rule/rm.ts
|
|
3131
|
-
import { Command as
|
|
3132
|
-
var recurrenceRuleDeleteCommand = new
|
|
3300
|
+
import { Command as Command50 } from "commander";
|
|
3301
|
+
var recurrenceRuleDeleteCommand = new Command50("rm").description("Delete a recurring todo rule").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
|
|
3133
3302
|
const client2 = await loadSdkClient();
|
|
3134
3303
|
const result = await recurrenceRuleDelete({
|
|
3135
3304
|
client: client2._rawClient,
|
|
@@ -3152,8 +3321,8 @@ var recurrenceRuleDeleteCommand = new Command49("rm").description("Delete a recu
|
|
|
3152
3321
|
});
|
|
3153
3322
|
|
|
3154
3323
|
// src/generated/cli/todo/rule/show.ts
|
|
3155
|
-
import { Command as
|
|
3156
|
-
var recurrenceRuleGetCommand = new
|
|
3324
|
+
import { Command as Command51 } from "commander";
|
|
3325
|
+
var recurrenceRuleGetCommand = new Command51("show").description("Get a recurring todo rule").argument("<id>", "id").action(async (id, opts) => {
|
|
3157
3326
|
const client2 = await loadSdkClient();
|
|
3158
3327
|
const result = await recurrenceRuleGet({
|
|
3159
3328
|
client: client2._rawClient,
|
|
@@ -3173,8 +3342,8 @@ var recurrenceRuleGetCommand = new Command50("show").description("Get a recurrin
|
|
|
3173
3342
|
});
|
|
3174
3343
|
|
|
3175
3344
|
// src/generated/cli/todo/rm.ts
|
|
3176
|
-
import { Command as
|
|
3177
|
-
var todoDeleteCommand = new
|
|
3345
|
+
import { Command as Command52 } from "commander";
|
|
3346
|
+
var todoDeleteCommand = new Command52("rm").description("Soft-delete a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
|
|
3178
3347
|
const client2 = await loadSdkClient();
|
|
3179
3348
|
const result = await todoDelete({
|
|
3180
3349
|
client: client2._rawClient,
|
|
@@ -3198,8 +3367,8 @@ var todoDeleteCommand = new Command51("rm").description("Soft-delete a todo").ar
|
|
|
3198
3367
|
});
|
|
3199
3368
|
|
|
3200
3369
|
// src/generated/cli/todo/show.ts
|
|
3201
|
-
import { Command as
|
|
3202
|
-
var todoGetCommand = new
|
|
3370
|
+
import { Command as Command53 } from "commander";
|
|
3371
|
+
var todoGetCommand = new Command53("show").description("Get a todo by id").argument("<id>", "id").option("--include-deleted <value>", "include_deleted").option("--include-orphan-fields <value>", "include_orphan_fields").action(async (id, opts) => {
|
|
3203
3372
|
const client2 = await loadSdkClient();
|
|
3204
3373
|
const result = await todoGet({
|
|
3205
3374
|
client: client2._rawClient,
|
|
@@ -3224,8 +3393,8 @@ var todoGetCommand = new Command52("show").description("Get a todo by id").argum
|
|
|
3224
3393
|
});
|
|
3225
3394
|
|
|
3226
3395
|
// src/generated/cli/todo/update.ts
|
|
3227
|
-
import { Command as
|
|
3228
|
-
var todoUpdateCommand = new
|
|
3396
|
+
import { Command as Command54 } from "commander";
|
|
3397
|
+
var todoUpdateCommand = new Command54("update").description("Update a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--title <value>", "New title. Omit to leave the existing title unchanged. Must be non-empty when supplied.").option("--description <value>", 'New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass empty string `""` explicitly to clear an existing description, or omit to leave unchanged. Passing `null` is strictly rejected.').option("--parent-id <value>", "Re-parent the todo. Pass a valid parent ID to attach under another todo, pass `null` to move it back to the root level, or omit to leave unchanged. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`.").option("--status <value>", "New status of the todo. Allowed transitions: `open` \u2794 `in_progress` \u2794 `done`. `cancelled` represents a terminal state. Transitioning to `done` automatically emits a `captureTodoCompleted` analytics event. Omit to leave the existing status unchanged.").option("--due-at <value>", 'Update calendar due date in ISO date-only format (`YYYY-MM-DD`). Pass `""` explicitly to clear an existing due date, or omit to leave it unchanged. Passing `null` is strictly rejected.').option("--type-id <value>", "Re-assign this todo to a different active type. The new type must belong to the todo's same project; otherwise the request fails with TYPE_PROJECT_MISMATCH. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "PATCH semantics: only the keys present in this map change. Pass `null` for a key (e.g. `custom_fields: { priority: null }`) to explicitly delete that custom field value. Array values are replaced wholesale with no element-level diff. Providing a key that is not declared on the effective todo type is rejected with `UNDECLARED_FIELD`.").option("--user-id <value>", "Reassign the owner (assignee) user ID of this todo. Target user must belong to the same organization.").action(async (id, opts) => {
|
|
3229
3398
|
const client2 = await loadSdkClient();
|
|
3230
3399
|
const result = await todoUpdate({
|
|
3231
3400
|
client: client2._rawClient,
|
|
@@ -3291,6 +3460,7 @@ function registerGeneratedCommands(root) {
|
|
|
3291
3460
|
const root_domain = root.command("domain").description("domain commands");
|
|
3292
3461
|
root_domain.addCommand(emailDomainCreateCommand);
|
|
3293
3462
|
root_domain.addCommand(emailDomainListCommand);
|
|
3463
|
+
root_domain.addCommand(emailDomainDeleteCommand);
|
|
3294
3464
|
root_domain.addCommand(emailDomainGetCommand);
|
|
3295
3465
|
root_domain.addCommand(emailDomainVerifyCommand);
|
|
3296
3466
|
const root_email = root.command("email").description("email commands");
|
|
@@ -3329,12 +3499,17 @@ function registerGeneratedCommands(root) {
|
|
|
3329
3499
|
}
|
|
3330
3500
|
|
|
3331
3501
|
// src/handwritten/commands/login.ts
|
|
3332
|
-
import { Command as
|
|
3502
|
+
import { Command as Command55 } from "commander";
|
|
3333
3503
|
|
|
3334
3504
|
// src/handwritten/auth/device-flow.ts
|
|
3335
3505
|
var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3336
3506
|
async function runDeviceFlow(opts) {
|
|
3337
|
-
const fetchImpl = opts.
|
|
3507
|
+
const fetchImpl = opts.store && opts.envName ? createConsistencyFetch({
|
|
3508
|
+
store: opts.store,
|
|
3509
|
+
envName: opts.envName,
|
|
3510
|
+
apiBase: opts.baseUrl,
|
|
3511
|
+
fetchImpl: opts.fetchImpl
|
|
3512
|
+
}) : opts.fetchImpl ?? fetch;
|
|
3338
3513
|
const sleep = opts.sleepMs ?? DEFAULT_SLEEP;
|
|
3339
3514
|
const codeRes = await fetchImpl(`${opts.baseUrl}/auth/oauth/device`, {
|
|
3340
3515
|
method: "POST",
|
|
@@ -3390,10 +3565,19 @@ async function runDeviceFlow(opts) {
|
|
|
3390
3565
|
var DEFAULT_CLIENT_NAME = "wspc CLI";
|
|
3391
3566
|
var DEFAULT_REDIRECT_URI = "http://localhost";
|
|
3392
3567
|
async function ensureClientId(opts) {
|
|
3393
|
-
const fetchImpl =
|
|
3568
|
+
const fetchImpl = createConsistencyFetch({
|
|
3569
|
+
store: opts.store,
|
|
3570
|
+
envName: opts.envName,
|
|
3571
|
+
apiBase: opts.baseUrl,
|
|
3572
|
+
fetchImpl: opts.fetchImpl
|
|
3573
|
+
});
|
|
3394
3574
|
const c = await opts.store.read();
|
|
3395
3575
|
const existing = c.envs[opts.envName]?.client_id;
|
|
3396
3576
|
if (existing) return existing;
|
|
3577
|
+
const targetEnv = c.envs[opts.envName] ??= { api_base: opts.baseUrl, accounts: {} };
|
|
3578
|
+
targetEnv.api_base = opts.baseUrl;
|
|
3579
|
+
targetEnv.accounts ??= {};
|
|
3580
|
+
await opts.store.write(c);
|
|
3397
3581
|
const res = await fetchImpl(`${opts.baseUrl}/auth/oauth/register`, {
|
|
3398
3582
|
method: "POST",
|
|
3399
3583
|
headers: { "content-type": "application/json" },
|
|
@@ -3419,7 +3603,12 @@ async function ensureClientId(opts) {
|
|
|
3419
3603
|
|
|
3420
3604
|
// src/handwritten/auth/fetch-me.ts
|
|
3421
3605
|
async function fetchMe(opts) {
|
|
3422
|
-
const f = opts.
|
|
3606
|
+
const f = opts.store && opts.envName ? createConsistencyFetch({
|
|
3607
|
+
store: opts.store,
|
|
3608
|
+
envName: opts.envName,
|
|
3609
|
+
apiBase: opts.baseUrl,
|
|
3610
|
+
fetchImpl: opts.fetchImpl
|
|
3611
|
+
}) : opts.fetchImpl ?? fetch;
|
|
3423
3612
|
const res = await f(`${opts.baseUrl}/auth/me`, {
|
|
3424
3613
|
headers: { authorization: `Bearer ${opts.token}` }
|
|
3425
3614
|
});
|
|
@@ -3448,7 +3637,16 @@ async function runLogin(opts) {
|
|
|
3448
3637
|
const now = opts.now ?? Date.now;
|
|
3449
3638
|
const me = opts.fetchMe ?? ((o) => fetchMe(o));
|
|
3450
3639
|
if (opts.apiKey) {
|
|
3451
|
-
const
|
|
3640
|
+
const initial = await opts.store.read();
|
|
3641
|
+
getOrCreateEnv(initial, envName, opts.baseUrl);
|
|
3642
|
+
initial.current_env = envName;
|
|
3643
|
+
await opts.store.write(initial);
|
|
3644
|
+
const who2 = await me({
|
|
3645
|
+
baseUrl: opts.baseUrl,
|
|
3646
|
+
token: opts.apiKey,
|
|
3647
|
+
store: opts.store,
|
|
3648
|
+
envName
|
|
3649
|
+
});
|
|
3452
3650
|
const c2 = await opts.store.read();
|
|
3453
3651
|
const env2 = getOrCreateEnv(c2, envName, opts.baseUrl);
|
|
3454
3652
|
const prev2 = env2.accounts[who2.email] ?? { email: who2.email };
|
|
@@ -3474,6 +3672,8 @@ async function runLogin(opts) {
|
|
|
3474
3672
|
const result = await flow({
|
|
3475
3673
|
baseUrl: opts.baseUrl,
|
|
3476
3674
|
clientId,
|
|
3675
|
+
store: opts.store,
|
|
3676
|
+
envName,
|
|
3477
3677
|
onPrompt: (p) => {
|
|
3478
3678
|
const prompt = p;
|
|
3479
3679
|
opts.output.writeJson({ event: "device_code_issued", ...prompt });
|
|
@@ -3486,7 +3686,12 @@ async function runLogin(opts) {
|
|
|
3486
3686
|
`);
|
|
3487
3687
|
}
|
|
3488
3688
|
});
|
|
3489
|
-
const who = await me({
|
|
3689
|
+
const who = await me({
|
|
3690
|
+
baseUrl: opts.baseUrl,
|
|
3691
|
+
token: result.access_token,
|
|
3692
|
+
store: opts.store,
|
|
3693
|
+
envName
|
|
3694
|
+
});
|
|
3490
3695
|
const c = await opts.store.read();
|
|
3491
3696
|
const env = getOrCreateEnv(c, envName, opts.baseUrl);
|
|
3492
3697
|
const prev = env.accounts[who.email] ?? { email: who.email };
|
|
@@ -3516,7 +3721,7 @@ function resolveLoginTarget(opts, env) {
|
|
|
3516
3721
|
function wantsJson(opts, env) {
|
|
3517
3722
|
return opts.json === true || env.WSPC_OUTPUT === "json";
|
|
3518
3723
|
}
|
|
3519
|
-
var loginCommand = new
|
|
3724
|
+
var loginCommand = new Command55("login").description("Log in via OAuth device flow (default) or API key").option("--api-key <key>", "Log in with a wspc API key (escape hatch)").option("--api-base <url>", "Target API base URL (default: production)").option("--env <name>", "Config env name to store credentials under").option("--json", "Emit machine-readable events to stdout").action(async (opts) => {
|
|
3520
3725
|
const store = new ConfigStore();
|
|
3521
3726
|
const { baseUrl, envName } = resolveLoginTarget(opts, process.env);
|
|
3522
3727
|
const output = wantsJson(opts, process.env) ? { write: () => {
|
|
@@ -3535,7 +3740,7 @@ var loginCommand = new Command54("login").description("Log in via OAuth device f
|
|
|
3535
3740
|
});
|
|
3536
3741
|
|
|
3537
3742
|
// src/handwritten/commands/logout.ts
|
|
3538
|
-
import { Command as
|
|
3743
|
+
import { Command as Command56 } from "commander";
|
|
3539
3744
|
|
|
3540
3745
|
// src/handwritten/auth/logout.ts
|
|
3541
3746
|
async function runLogout(opts) {
|
|
@@ -3563,7 +3768,7 @@ async function runLogout(opts) {
|
|
|
3563
3768
|
}
|
|
3564
3769
|
|
|
3565
3770
|
// src/handwritten/commands/logout.ts
|
|
3566
|
-
var logoutCommand = new
|
|
3771
|
+
var logoutCommand = new Command56("logout").description("Log out an account (default: the active account in the current env)").argument("[email]", "Email of the account to log out").option("--all", "Log out every account in the current env").action(async (email, opts) => {
|
|
3567
3772
|
const res = await runLogout({ store: new ConfigStore(), email, all: opts.all });
|
|
3568
3773
|
if (res.removed.length === 0) {
|
|
3569
3774
|
process.stdout.write("nothing to log out\n");
|
|
@@ -3576,7 +3781,7 @@ var logoutCommand = new Command55("logout").description("Log out an account (def
|
|
|
3576
3781
|
});
|
|
3577
3782
|
|
|
3578
3783
|
// src/handwritten/commands/whoami.ts
|
|
3579
|
-
import { Command as
|
|
3784
|
+
import { Command as Command57 } from "commander";
|
|
3580
3785
|
var ENV_DISPLAY = {
|
|
3581
3786
|
shape: "object",
|
|
3582
3787
|
fields: ["name", "api_base", "account", "actor", "agent_label"]
|
|
@@ -3608,7 +3813,7 @@ async function backfillActiveEmail(store, envName, email, userId) {
|
|
|
3608
3813
|
await store.write(cfg);
|
|
3609
3814
|
}
|
|
3610
3815
|
}
|
|
3611
|
-
var whoamiCommand = new
|
|
3816
|
+
var whoamiCommand = new Command57("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
|
|
3612
3817
|
const store = new ConfigStore();
|
|
3613
3818
|
const config = await store.read();
|
|
3614
3819
|
let resolved;
|
|
@@ -3661,8 +3866,8 @@ function printLoggedOut() {
|
|
|
3661
3866
|
}
|
|
3662
3867
|
|
|
3663
3868
|
// src/handwritten/commands/config.ts
|
|
3664
|
-
import { Command as
|
|
3665
|
-
var configCommand = new
|
|
3869
|
+
import { Command as Command58 } from "commander";
|
|
3870
|
+
var configCommand = new Command58("config").description("Manage wspc local config");
|
|
3666
3871
|
registerRenderer("config_show", (data) => {
|
|
3667
3872
|
const d = data;
|
|
3668
3873
|
if (d.envs.length === 0) {
|
|
@@ -3733,7 +3938,7 @@ configCommand.command("use <env>").description("Switch current_env").action(asyn
|
|
|
3733
3938
|
});
|
|
3734
3939
|
|
|
3735
3940
|
// src/handwritten/commands/account.ts
|
|
3736
|
-
import { Command as
|
|
3941
|
+
import { Command as Command59 } from "commander";
|
|
3737
3942
|
async function listAccounts(store) {
|
|
3738
3943
|
const c = await store.read();
|
|
3739
3944
|
const envName = c.current_env;
|
|
@@ -3774,7 +3979,7 @@ registerRenderer("account_ls", (data) => {
|
|
|
3774
3979
|
]);
|
|
3775
3980
|
process.stdout.write(table(headers, body));
|
|
3776
3981
|
});
|
|
3777
|
-
var accountCommand = new
|
|
3982
|
+
var accountCommand = new Command59("account").description("Manage logged-in accounts");
|
|
3778
3983
|
accountCommand.command("ls").description("List accounts in the current env (active marked with \u2713)").action(async () => {
|
|
3779
3984
|
const accounts = await listAccounts(new ConfigStore());
|
|
3780
3985
|
render({ kind: "account_ls" }, { accounts });
|
|
@@ -3786,7 +3991,7 @@ accountCommand.command("switch <email>").description("Set the active account for
|
|
|
3786
3991
|
});
|
|
3787
3992
|
|
|
3788
3993
|
// src/handwritten/commands/todo-done.ts
|
|
3789
|
-
import { Command as
|
|
3994
|
+
import { Command as Command60 } from "commander";
|
|
3790
3995
|
var TODO_UPDATE_DISPLAY = {
|
|
3791
3996
|
shape: "object",
|
|
3792
3997
|
format: {
|
|
@@ -3804,7 +4009,7 @@ var TODO_UPDATE_DISPLAY = {
|
|
|
3804
4009
|
deleted_at: "relative-time"
|
|
3805
4010
|
}
|
|
3806
4011
|
};
|
|
3807
|
-
var todoDoneCommand = new
|
|
4012
|
+
var todoDoneCommand = new Command60("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
|
|
3808
4013
|
const client2 = await loadSdkClient();
|
|
3809
4014
|
const result = await todoUpdate({
|
|
3810
4015
|
client: client2._rawClient,
|
|
@@ -3823,7 +4028,7 @@ var todoDoneCommand = new Command59("done").description("Mark a todo done (sugar
|
|
|
3823
4028
|
});
|
|
3824
4029
|
|
|
3825
4030
|
// src/handwritten/commands/email/send.ts
|
|
3826
|
-
import { Command as
|
|
4031
|
+
import { Command as Command61 } from "commander";
|
|
3827
4032
|
import { readFile, stat } from "fs/promises";
|
|
3828
4033
|
import { basename } from "path";
|
|
3829
4034
|
|
|
@@ -3881,7 +4086,7 @@ async function resolveAttachment(input) {
|
|
|
3881
4086
|
`--attach ${input}: neither a readable file nor a valid <prefix>_<ulid>:<idx> reference.`
|
|
3882
4087
|
);
|
|
3883
4088
|
}
|
|
3884
|
-
var sendCommand = new
|
|
4089
|
+
var sendCommand = new Command61("send").description("Send an outbound email").requiredOption("--from <alias-email>", "alias email to send from").option("--to <addr...>", "recipient address (repeatable)", []).option("--subject <text>", "subject").option("--text <body>", "plain-text body").option("--text-file <path>", "read text body from file").option("--reply <id>", "inbound email id to reply to").option("--attach <path-or-ref...>", "attachment (file path or eml_xxx:idx)", []).requiredOption("--idempotency-key <key>", "idempotency key").action(async (opts) => {
|
|
3885
4090
|
const isReply = Boolean(opts.reply);
|
|
3886
4091
|
const to = opts.to;
|
|
3887
4092
|
const attachInputs = opts.attach;
|
|
@@ -3968,7 +4173,7 @@ var sendCommand = new Command60("send").description("Send an outbound email").re
|
|
|
3968
4173
|
});
|
|
3969
4174
|
|
|
3970
4175
|
// src/handwritten/commands/email/attachment.ts
|
|
3971
|
-
import { Command as
|
|
4176
|
+
import { Command as Command62 } from "commander";
|
|
3972
4177
|
import { createWriteStream } from "fs";
|
|
3973
4178
|
import { Readable } from "stream";
|
|
3974
4179
|
import { pipeline } from "stream/promises";
|
|
@@ -3985,7 +4190,7 @@ function parseContentDispositionFilename(header) {
|
|
|
3985
4190
|
}
|
|
3986
4191
|
|
|
3987
4192
|
// src/handwritten/commands/email/attachment.ts
|
|
3988
|
-
var attachmentCommand = new
|
|
4193
|
+
var attachmentCommand = new Command62("attachment").description("Download an inbound email attachment by index").argument("<email-id>").argument("<idx>").option("--output <path>", "output file path").option("--include-deleted", "allow downloads from soft-deleted parent emails").action(async (emailId, idxArg, opts) => {
|
|
3989
4194
|
const idx = Number(idxArg);
|
|
3990
4195
|
if (!Number.isInteger(idx) || idx < 0) {
|
|
3991
4196
|
process.stderr.write(`<idx> must be a non-negative integer (got "${idxArg}")
|
|
@@ -4018,7 +4223,7 @@ var attachmentCommand = new Command61("attachment").description("Download an inb
|
|
|
4018
4223
|
|
|
4019
4224
|
// src/cli.ts
|
|
4020
4225
|
function buildProgram() {
|
|
4021
|
-
const program = new
|
|
4226
|
+
const program = new Command63().name("wspc").description("Official CLI for wspc.ai").version(`wspc ${VERSION} (spec ${SPEC_SHA}, fetched ${SPEC_FETCHED_AT})`).option("--json", "Output raw JSON (machine-readable)").option("--account <email>", "Run as a specific account (overrides the active account)").hook("preAction", (_thisCommand, actionCommand) => {
|
|
4022
4227
|
const globals = actionCommand.optsWithGlobals();
|
|
4023
4228
|
if (globals.json) process.env.WSPC_OUTPUT = "json";
|
|
4024
4229
|
if (globals.account) process.env.WSPC_ACCOUNT = String(globals.account);
|