@ait-co/console-cli 0.1.23 → 0.1.24
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.mjs +502 -213
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -164,6 +164,48 @@ async function executeAndUnwrap(url, init, fetchImpl) {
|
|
|
164
164
|
throw new TossApiError(res.status, parsed.error.errorCode, parsed.error.reason, parsed.error.errorType);
|
|
165
165
|
}
|
|
166
166
|
//#endregion
|
|
167
|
+
//#region src/api/certs.ts
|
|
168
|
+
const BASE$5 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
169
|
+
async function fetchCerts(workspaceId, miniAppId, cookies, opts = {}) {
|
|
170
|
+
const raw = await requestConsoleApi({
|
|
171
|
+
url: `${BASE$5}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs`,
|
|
172
|
+
cookies,
|
|
173
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
174
|
+
});
|
|
175
|
+
if (!Array.isArray(raw)) throw new Error(`Unexpected certs shape for app=${miniAppId}: not an array`);
|
|
176
|
+
return raw.map((c) => {
|
|
177
|
+
if (c === null || typeof c !== "object") return {};
|
|
178
|
+
return c;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async function issueCert(workspaceId, miniAppId, name, cookies, opts = {}) {
|
|
182
|
+
const raw = await requestConsoleApi({
|
|
183
|
+
url: `${BASE$5}/workspaces/${workspaceId}/mini-app/${miniAppId}/cert/issue`,
|
|
184
|
+
method: "POST",
|
|
185
|
+
body: { name },
|
|
186
|
+
cookies,
|
|
187
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
188
|
+
});
|
|
189
|
+
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected issue-cert shape for app=${miniAppId}: not an object`);
|
|
190
|
+
const rec = raw;
|
|
191
|
+
const privateKey = typeof rec.privateKey === "string" ? rec.privateKey : null;
|
|
192
|
+
const publicKey = typeof rec.publicKey === "string" ? rec.publicKey : null;
|
|
193
|
+
if (privateKey === null || publicKey === null) throw new Error(`Unexpected issue-cert shape for app=${miniAppId}: missing privateKey/publicKey`);
|
|
194
|
+
return {
|
|
195
|
+
privateKey,
|
|
196
|
+
publicKey
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
async function revokeCert(workspaceId, miniAppId, certId, cookies, opts = {}) {
|
|
200
|
+
await requestConsoleApi({
|
|
201
|
+
url: `${BASE$5}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs/${encodeURIComponent(certId)}/disable`,
|
|
202
|
+
method: "POST",
|
|
203
|
+
body: {},
|
|
204
|
+
cookies,
|
|
205
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
167
209
|
//#region src/api/mini-apps.ts
|
|
168
210
|
const BASE$4 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
169
211
|
async function fetchMiniApps(workspaceId, cookies, opts = {}) {
|
|
@@ -329,45 +371,6 @@ async function fetchConversionMetrics(params, cookies, opts = {}) {
|
|
|
329
371
|
cacheTime: typeof rec.cacheTime === "string" ? rec.cacheTime : void 0
|
|
330
372
|
};
|
|
331
373
|
}
|
|
332
|
-
async function fetchCerts(workspaceId, miniAppId, cookies, opts = {}) {
|
|
333
|
-
const raw = await requestConsoleApi({
|
|
334
|
-
url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs`,
|
|
335
|
-
cookies,
|
|
336
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
337
|
-
});
|
|
338
|
-
if (!Array.isArray(raw)) throw new Error(`Unexpected certs shape for app=${miniAppId}: not an array`);
|
|
339
|
-
return raw.map((c) => {
|
|
340
|
-
if (c === null || typeof c !== "object") return {};
|
|
341
|
-
return c;
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
async function issueCert(workspaceId, miniAppId, name, cookies, opts = {}) {
|
|
345
|
-
const raw = await requestConsoleApi({
|
|
346
|
-
url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/cert/issue`,
|
|
347
|
-
method: "POST",
|
|
348
|
-
body: { name },
|
|
349
|
-
cookies,
|
|
350
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
351
|
-
});
|
|
352
|
-
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected issue-cert shape for app=${miniAppId}: not an object`);
|
|
353
|
-
const rec = raw;
|
|
354
|
-
const privateKey = typeof rec.privateKey === "string" ? rec.privateKey : null;
|
|
355
|
-
const publicKey = typeof rec.publicKey === "string" ? rec.publicKey : null;
|
|
356
|
-
if (privateKey === null || publicKey === null) throw new Error(`Unexpected issue-cert shape for app=${miniAppId}: missing privateKey/publicKey`);
|
|
357
|
-
return {
|
|
358
|
-
privateKey,
|
|
359
|
-
publicKey
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
async function revokeCert(workspaceId, miniAppId, certId, cookies, opts = {}) {
|
|
363
|
-
await requestConsoleApi({
|
|
364
|
-
url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs/${encodeURIComponent(certId)}/disable`,
|
|
365
|
-
method: "POST",
|
|
366
|
-
body: {},
|
|
367
|
-
cookies,
|
|
368
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
374
|
async function fetchShareRewards(params, cookies, opts = {}) {
|
|
372
375
|
const qs = new URLSearchParams();
|
|
373
376
|
qs.set("search", params.search ?? "");
|
|
@@ -1587,6 +1590,153 @@ function printContextHeader(ctx, opts) {
|
|
|
1587
1590
|
process.stderr.write(line);
|
|
1588
1591
|
}
|
|
1589
1592
|
//#endregion
|
|
1593
|
+
//#region src/api/me.ts
|
|
1594
|
+
const BASE$3 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
1595
|
+
const MEMBER_USER_INFO_URL = `${BASE$3}/members/me/user-info`;
|
|
1596
|
+
async function fetchConsoleMemberUserInfo(cookies, opts = {}) {
|
|
1597
|
+
return requestConsoleApi({
|
|
1598
|
+
url: MEMBER_USER_INFO_URL,
|
|
1599
|
+
cookies,
|
|
1600
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
async function fetchUserTerms(cookies, opts = {}) {
|
|
1604
|
+
const raw = await requestConsoleApi({
|
|
1605
|
+
url: `${BASE$3}/console-user-terms/me`,
|
|
1606
|
+
cookies,
|
|
1607
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1608
|
+
});
|
|
1609
|
+
if (!Array.isArray(raw)) throw new Error("Unexpected user-terms shape: not an array");
|
|
1610
|
+
return raw.map((entry, i) => {
|
|
1611
|
+
if (!entry || typeof entry !== "object") throw new Error(`Unexpected user-terms entry at index ${i}`);
|
|
1612
|
+
const e = entry;
|
|
1613
|
+
return {
|
|
1614
|
+
required: Boolean(e.required),
|
|
1615
|
+
termsId: typeof e.termsId === "number" ? e.termsId : 0,
|
|
1616
|
+
revisionId: typeof e.revisionId === "number" ? e.revisionId : 0,
|
|
1617
|
+
title: typeof e.title === "string" ? e.title : "",
|
|
1618
|
+
contentsUrl: typeof e.contentsUrl === "string" ? e.contentsUrl : "",
|
|
1619
|
+
actionType: typeof e.actionType === "string" ? e.actionType : "",
|
|
1620
|
+
isAgreed: Boolean(e.isAgreed),
|
|
1621
|
+
isOneTimeConsent: Boolean(e.isOneTimeConsent)
|
|
1622
|
+
};
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
//#endregion
|
|
1626
|
+
//#region src/api/workspaces.ts
|
|
1627
|
+
const WORKSPACES_BASE = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
1628
|
+
async function fetchWorkspaceDetail(workspaceId, cookies, opts = {}) {
|
|
1629
|
+
const raw = await requestConsoleApi({
|
|
1630
|
+
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}`,
|
|
1631
|
+
cookies,
|
|
1632
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1633
|
+
});
|
|
1634
|
+
const id = raw.id;
|
|
1635
|
+
const name = raw.name;
|
|
1636
|
+
if (typeof id !== "number" || !Number.isInteger(id) || id <= 0 || typeof name !== "string") throw new Error(`Unexpected workspace detail shape for id=${workspaceId}`);
|
|
1637
|
+
const { id: _id, name: _name, ...extra } = raw;
|
|
1638
|
+
return {
|
|
1639
|
+
workspaceId: id,
|
|
1640
|
+
workspaceName: name,
|
|
1641
|
+
extra
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
async function fetchWorkspacePartner(workspaceId, cookies, opts = {}) {
|
|
1645
|
+
const raw = await requestConsoleApi({
|
|
1646
|
+
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/partner`,
|
|
1647
|
+
cookies,
|
|
1648
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1649
|
+
});
|
|
1650
|
+
const registered = raw.registered;
|
|
1651
|
+
if (typeof registered !== "boolean") throw new Error(`Unexpected workspace partner shape for id=${workspaceId}`);
|
|
1652
|
+
return {
|
|
1653
|
+
registered,
|
|
1654
|
+
approvalType: typeof raw.approvalType === "string" ? raw.approvalType : null,
|
|
1655
|
+
rejectMessage: typeof raw.rejectMessage === "string" ? raw.rejectMessage : null,
|
|
1656
|
+
partner: raw.partner && typeof raw.partner === "object" ? raw.partner : null
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
const WORKSPACE_TERM_TYPES = [
|
|
1660
|
+
"TOSS_LOGIN",
|
|
1661
|
+
"BIZ_WORKSPACE",
|
|
1662
|
+
"TOSS_PROMOTION_MONEY",
|
|
1663
|
+
"IAA",
|
|
1664
|
+
"IAP"
|
|
1665
|
+
];
|
|
1666
|
+
const DEFAULT_SEGMENT_CATEGORY = "생성된 세그먼트";
|
|
1667
|
+
async function fetchWorkspaceSegments(params, cookies, opts = {}) {
|
|
1668
|
+
const page = params.page ?? 0;
|
|
1669
|
+
const qs = new URLSearchParams();
|
|
1670
|
+
qs.set("category", params.category ?? DEFAULT_SEGMENT_CATEGORY);
|
|
1671
|
+
qs.set("search", params.search ?? "");
|
|
1672
|
+
qs.set("page", String(page));
|
|
1673
|
+
const raw = await requestConsoleApi({
|
|
1674
|
+
url: `${WORKSPACES_BASE}/workspaces/${params.workspaceId}/segments/list?${qs.toString()}`,
|
|
1675
|
+
cookies,
|
|
1676
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1677
|
+
});
|
|
1678
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected segments shape for workspace=${params.workspaceId}`);
|
|
1679
|
+
const data = raw;
|
|
1680
|
+
return {
|
|
1681
|
+
contents: (Array.isArray(data.contents) ? data.contents : []).map((c) => c && typeof c === "object" ? c : {}),
|
|
1682
|
+
totalPage: typeof data.totalPage === "number" ? data.totalPage : 0,
|
|
1683
|
+
currentPage: typeof data.currentPage === "number" ? data.currentPage : page
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
async function fetchWorkspaceTerms(workspaceId, type, cookies, opts = {}) {
|
|
1687
|
+
const raw = await requestConsoleApi({
|
|
1688
|
+
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms/${type}/skip-permission`,
|
|
1689
|
+
cookies,
|
|
1690
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1691
|
+
});
|
|
1692
|
+
if (!Array.isArray(raw)) throw new Error(`Unexpected workspace terms shape for type=${type}`);
|
|
1693
|
+
return raw.map((entry, i) => {
|
|
1694
|
+
if (!entry || typeof entry !== "object") throw new Error(`Unexpected workspace terms entry at index ${i} for type=${type}`);
|
|
1695
|
+
const e = entry;
|
|
1696
|
+
return {
|
|
1697
|
+
required: Boolean(e.required),
|
|
1698
|
+
termsId: typeof e.termsId === "number" ? e.termsId : 0,
|
|
1699
|
+
revisionId: typeof e.revisionId === "number" ? e.revisionId : 0,
|
|
1700
|
+
title: typeof e.title === "string" ? e.title : "",
|
|
1701
|
+
contentsUrl: typeof e.contentsUrl === "string" ? e.contentsUrl : "",
|
|
1702
|
+
actionType: typeof e.actionType === "string" ? e.actionType : "",
|
|
1703
|
+
isAgreed: Boolean(e.isAgreed),
|
|
1704
|
+
isOneTimeConsent: Boolean(e.isOneTimeConsent)
|
|
1705
|
+
};
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Persist agreement for one-or-more workspace terms. The endpoint takes a
|
|
1710
|
+
* single `agreedList` regardless of which bucket the terms came from — the
|
|
1711
|
+
* type tag is implicit in the (termsId, revisionId) pairs.
|
|
1712
|
+
*
|
|
1713
|
+
* Captured behaviour (2026-05-08, ws=36577):
|
|
1714
|
+
* - `POST /workspaces/<wid>/console-workspace-terms` with body
|
|
1715
|
+
* `{"agreedList":[{"termsId": <int>, "revisionId": <int>}, ...]}`
|
|
1716
|
+
* - Response on success: `{"resultType":"SUCCESS","success":{}}` — no
|
|
1717
|
+
* useful payload. We resolve `void`.
|
|
1718
|
+
* - Re-submitting an already-agreed term returns `errorCode: 500`
|
|
1719
|
+
* (Internal Server Error). The server is NOT idempotent, so callers
|
|
1720
|
+
* must filter to `isAgreed === false` before invoking.
|
|
1721
|
+
* - Empty `agreedList` returns SUCCESS (no-op), but we throw client-side
|
|
1722
|
+
* before sending — round-tripping a no-op request is wasted.
|
|
1723
|
+
*
|
|
1724
|
+
* Failure surfaces through `TossApiError` like every other write helper.
|
|
1725
|
+
*/
|
|
1726
|
+
async function agreeWorkspaceTerms(workspaceId, terms, cookies, opts = {}) {
|
|
1727
|
+
if (terms.length === 0) throw new Error("agreeWorkspaceTerms requires at least one term");
|
|
1728
|
+
await requestConsoleApi({
|
|
1729
|
+
method: "POST",
|
|
1730
|
+
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms`,
|
|
1731
|
+
cookies,
|
|
1732
|
+
body: { agreedList: terms.map(({ termsId, revisionId }) => ({
|
|
1733
|
+
termsId,
|
|
1734
|
+
revisionId
|
|
1735
|
+
})) },
|
|
1736
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
//#endregion
|
|
1590
1740
|
//#region src/config/ait-bundle.ts
|
|
1591
1741
|
var AitBundleError = class extends Error {
|
|
1592
1742
|
path;
|
|
@@ -1878,30 +2028,21 @@ async function runDeploy(args, deps = {}) {
|
|
|
1878
2028
|
const steps = ["upload"];
|
|
1879
2029
|
if (requestReview) steps.push("review");
|
|
1880
2030
|
if (release) steps.push("release");
|
|
1881
|
-
if (args.dryRun) {
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
const stepsLine = steps.map((s) => {
|
|
1897
|
-
if (s === "review") return `review (releaseNotes: ${JSON.stringify(releaseNotes ?? "")})`;
|
|
1898
|
-
if (s === "release") return `release (${confirm ? "confirmed" : "NOT confirmed"})`;
|
|
1899
|
-
return s;
|
|
1900
|
-
}).join(" → ");
|
|
1901
|
-
process.stdout.write(`DRY RUN\n app ${appId}\n workspace ${workspaceId}\n bundle ${args.path} (${bundleInfo.bytes.byteLength} bytes)\n deploymentId ${deploymentId}\n memo ${memo ?? "(none)"}\n steps ${stepsLine}\n`);
|
|
1902
|
-
}
|
|
1903
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
1904
|
-
}
|
|
2031
|
+
if (args.dryRun) return runDryRun({
|
|
2032
|
+
json: args.json,
|
|
2033
|
+
path: args.path,
|
|
2034
|
+
bundleInfo,
|
|
2035
|
+
deploymentId,
|
|
2036
|
+
explicitDeploymentId: typeof args.deploymentId === "string" && args.deploymentId !== "",
|
|
2037
|
+
workspaceId,
|
|
2038
|
+
appId,
|
|
2039
|
+
session,
|
|
2040
|
+
steps,
|
|
2041
|
+
memo,
|
|
2042
|
+
releaseNotes,
|
|
2043
|
+
confirm,
|
|
2044
|
+
fetchImpl: deps.fetchImpl
|
|
2045
|
+
});
|
|
1905
2046
|
const apiOpts = deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {};
|
|
1906
2047
|
let uploaded = false;
|
|
1907
2048
|
let bundleRecord = null;
|
|
@@ -2037,38 +2178,154 @@ async function emitPartialFailure(json, err, progress) {
|
|
|
2037
2178
|
else process.stderr.write(`Unexpected error: ${err.message}\n`);
|
|
2038
2179
|
return exitAfterFlush(ExitCode.ApiError);
|
|
2039
2180
|
}
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2181
|
+
const WORKSPACE_TERM_ERROR_CODES = {
|
|
2182
|
+
TOSS_LOGIN: 4037,
|
|
2183
|
+
BIZ_WORKSPACE: 4040,
|
|
2184
|
+
TOSS_PROMOTION_MONEY: 4039,
|
|
2185
|
+
IAA: 4099,
|
|
2186
|
+
IAP: 5001
|
|
2187
|
+
};
|
|
2188
|
+
async function runDryRun(input) {
|
|
2189
|
+
const apiOpts = input.fetchImpl ? { fetchImpl: input.fetchImpl } : {};
|
|
2190
|
+
const embedded = input.bundleInfo.deploymentId;
|
|
2191
|
+
const flagMatch = input.explicitDeploymentId ? input.deploymentId === embedded : null;
|
|
2192
|
+
const [permissions, terms] = await Promise.all([fetchPermissions(input.workspaceId, input.session, apiOpts), fetchTermsBlockers(input.workspaceId, input.session, apiOpts)]);
|
|
2193
|
+
const wouldSucceed = (flagMatch === null || flagMatch === true) && terms.blockers.length === 0;
|
|
2194
|
+
if (input.json) {
|
|
2195
|
+
emitJson({
|
|
2196
|
+
ok: true,
|
|
2197
|
+
dryRun: true,
|
|
2198
|
+
wouldSucceed,
|
|
2199
|
+
workspaceId: input.workspaceId,
|
|
2200
|
+
appId: input.appId,
|
|
2201
|
+
deploymentId: input.deploymentId,
|
|
2202
|
+
bundleFormat: input.bundleInfo.format,
|
|
2203
|
+
bytes: input.bundleInfo.bytes.byteLength,
|
|
2204
|
+
steps: input.steps,
|
|
2205
|
+
memo: input.memo ?? null,
|
|
2206
|
+
releaseNotes: input.releaseNotes ?? null,
|
|
2207
|
+
confirmed: input.confirm,
|
|
2208
|
+
bundle: {
|
|
2209
|
+
path: input.path,
|
|
2210
|
+
format: input.bundleInfo.format,
|
|
2211
|
+
deploymentId: input.deploymentId,
|
|
2212
|
+
embeddedDeploymentId: embedded,
|
|
2213
|
+
deploymentIdSource: input.explicitDeploymentId ? "flag" : "bundle",
|
|
2214
|
+
flagMatch,
|
|
2215
|
+
size: input.bundleInfo.bytes.byteLength
|
|
2216
|
+
},
|
|
2217
|
+
context: {
|
|
2218
|
+
workspaceId: input.workspaceId,
|
|
2219
|
+
appId: input.appId,
|
|
2220
|
+
sessionValid: true,
|
|
2221
|
+
permissions
|
|
2222
|
+
},
|
|
2223
|
+
terms
|
|
2224
|
+
});
|
|
2225
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2226
|
+
}
|
|
2227
|
+
process.stdout.write(renderDryRunText(input, {
|
|
2228
|
+
embedded,
|
|
2229
|
+
flagMatch,
|
|
2230
|
+
permissions,
|
|
2231
|
+
terms,
|
|
2232
|
+
wouldSucceed
|
|
2233
|
+
}));
|
|
2234
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2050
2235
|
}
|
|
2051
|
-
async function
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
if (!entry || typeof entry !== "object") throw new Error(`Unexpected user-terms entry at index ${i}`);
|
|
2060
|
-
const e = entry;
|
|
2236
|
+
async function fetchPermissions(workspaceId, session, apiOpts) {
|
|
2237
|
+
try {
|
|
2238
|
+
const ws = (await fetchConsoleMemberUserInfo(session.cookies, apiOpts)).workspaces.find((w) => w.workspaceId === workspaceId);
|
|
2239
|
+
if (!ws) return {
|
|
2240
|
+
role: null,
|
|
2241
|
+
source: "unknown",
|
|
2242
|
+
error: `current user has no membership in workspace ${workspaceId}`
|
|
2243
|
+
};
|
|
2061
2244
|
return {
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
revisionId: typeof e.revisionId === "number" ? e.revisionId : 0,
|
|
2065
|
-
title: typeof e.title === "string" ? e.title : "",
|
|
2066
|
-
contentsUrl: typeof e.contentsUrl === "string" ? e.contentsUrl : "",
|
|
2067
|
-
actionType: typeof e.actionType === "string" ? e.actionType : "",
|
|
2068
|
-
isAgreed: Boolean(e.isAgreed),
|
|
2069
|
-
isOneTimeConsent: Boolean(e.isOneTimeConsent)
|
|
2245
|
+
role: ws.role,
|
|
2246
|
+
source: "members/me"
|
|
2070
2247
|
};
|
|
2071
|
-
})
|
|
2248
|
+
} catch (err) {
|
|
2249
|
+
return {
|
|
2250
|
+
role: null,
|
|
2251
|
+
source: "unknown",
|
|
2252
|
+
error: err.message
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
async function fetchTermsBlockers(workspaceId, session, apiOpts) {
|
|
2257
|
+
try {
|
|
2258
|
+
const [userTerms, workspaceResults] = await Promise.all([fetchUserTerms(session.cookies, apiOpts), Promise.all(WORKSPACE_TERM_TYPES.map(async (t) => [t, await fetchWorkspaceTerms(workspaceId, t, session.cookies, apiOpts)]))]);
|
|
2259
|
+
const blockers = [];
|
|
2260
|
+
for (const t of userTerms) if (t.required && !t.isAgreed) blockers.push({
|
|
2261
|
+
scope: "user",
|
|
2262
|
+
type: "USER_TERMS",
|
|
2263
|
+
errorCode: 4036,
|
|
2264
|
+
title: t.title,
|
|
2265
|
+
action: "aitcc me terms"
|
|
2266
|
+
});
|
|
2267
|
+
for (const [type, terms] of workspaceResults) for (const t of terms) {
|
|
2268
|
+
if (!t.required || t.isAgreed) continue;
|
|
2269
|
+
blockers.push({
|
|
2270
|
+
scope: "workspace",
|
|
2271
|
+
type,
|
|
2272
|
+
errorCode: WORKSPACE_TERM_ERROR_CODES[type],
|
|
2273
|
+
title: t.title,
|
|
2274
|
+
action: `aitcc workspace terms --type ${type}`
|
|
2275
|
+
});
|
|
2276
|
+
}
|
|
2277
|
+
return {
|
|
2278
|
+
blockers,
|
|
2279
|
+
checked: true
|
|
2280
|
+
};
|
|
2281
|
+
} catch (err) {
|
|
2282
|
+
return {
|
|
2283
|
+
blockers: [],
|
|
2284
|
+
checked: false,
|
|
2285
|
+
error: err.message
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
function renderDryRunText(input, derived) {
|
|
2290
|
+
const lines = [];
|
|
2291
|
+
lines.push(`DRY RUN — app deploy ${input.appId}\n`);
|
|
2292
|
+
lines.push("\nBundle\n");
|
|
2293
|
+
lines.push(` path ${input.path}\n`);
|
|
2294
|
+
lines.push(` format ${input.bundleInfo.format.toUpperCase()}\n`);
|
|
2295
|
+
lines.push(` deploymentId ${input.deploymentId}\n`);
|
|
2296
|
+
if (derived.flagMatch === false) lines.push(` flag match MISMATCH (bundle embeds ${derived.embedded})\n`);
|
|
2297
|
+
else if (derived.flagMatch === true) lines.push(` flag match ok (matches embedded)\n`);
|
|
2298
|
+
lines.push(` size ${formatBytes(input.bundleInfo.bytes.byteLength)}\n`);
|
|
2299
|
+
lines.push("\nContext\n");
|
|
2300
|
+
lines.push(` workspace ${input.workspaceId}\n`);
|
|
2301
|
+
lines.push(` app ${input.appId}\n`);
|
|
2302
|
+
lines.push(` session valid\n`);
|
|
2303
|
+
if (derived.permissions.role !== null) lines.push(` permissions ${derived.permissions.role}\n`);
|
|
2304
|
+
else lines.push(` permissions unknown${derived.permissions.error ? ` (${derived.permissions.error})` : ""}\n`);
|
|
2305
|
+
lines.push("\nTerms\n");
|
|
2306
|
+
if (!derived.terms.checked) {
|
|
2307
|
+
lines.push(` warning: could not check terms status (${derived.terms.error ?? "unknown error"}).\n`);
|
|
2308
|
+
lines.push(" live deploy may still fail with a 4032/4036/4037/4039/4040/4099/5001 errorCode.\n");
|
|
2309
|
+
} else if (derived.terms.blockers.length === 0) lines.push(" all deploy-related terms are agreed\n");
|
|
2310
|
+
else for (const b of derived.terms.blockers) lines.push(` blocked: ${b.scope}/${b.type} — ${b.title} (errorCode ${b.errorCode})\n action: ${b.action}\n`);
|
|
2311
|
+
lines.push("\nPlan\n");
|
|
2312
|
+
const stepsLine = input.steps.map((s) => {
|
|
2313
|
+
if (s === "review") return `review (releaseNotes: ${JSON.stringify(input.releaseNotes ?? "")})`;
|
|
2314
|
+
if (s === "release") return `release (${input.confirm ? "confirmed" : "NOT confirmed"})`;
|
|
2315
|
+
return s;
|
|
2316
|
+
}).join(" → ");
|
|
2317
|
+
lines.push(` steps ${stepsLine}\n`);
|
|
2318
|
+
lines.push(` memo ${input.memo ?? "(none)"}\n`);
|
|
2319
|
+
lines.push("\nResult\n");
|
|
2320
|
+
if (!derived.wouldSucceed) lines.push(" Live deploy would fail. Resolve the blocked items above, then re-run.\n");
|
|
2321
|
+
else if (derived.permissions.role === null) lines.push(" Live deploy would clear bundle + terms checks. Workspace membership could not be confirmed; live deploy may still fail with a permissions error.\n");
|
|
2322
|
+
else lines.push(" Live deploy would clear every pre-flight check.\n");
|
|
2323
|
+
return lines.join("");
|
|
2324
|
+
}
|
|
2325
|
+
function formatBytes(n) {
|
|
2326
|
+
if (n < 1024) return `${n} B`;
|
|
2327
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
2328
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
2072
2329
|
}
|
|
2073
2330
|
//#endregion
|
|
2074
2331
|
//#region src/config/app-manifest.ts
|
|
@@ -4296,6 +4553,25 @@ const bundlesCommand = defineCommand({
|
|
|
4296
4553
|
})
|
|
4297
4554
|
}
|
|
4298
4555
|
});
|
|
4556
|
+
const CERT_EXPIRY_WARN_DAYS = 30;
|
|
4557
|
+
const MS_PER_DAY = 864e5;
|
|
4558
|
+
function deriveDaysUntilExpiry(cert, now) {
|
|
4559
|
+
const raw = cert.expireTs;
|
|
4560
|
+
let ts = null;
|
|
4561
|
+
if (typeof raw === "number" && Number.isFinite(raw)) ts = raw;
|
|
4562
|
+
else if (typeof raw === "string") {
|
|
4563
|
+
const parsed = Date.parse(raw);
|
|
4564
|
+
if (Number.isFinite(parsed)) ts = parsed;
|
|
4565
|
+
}
|
|
4566
|
+
if (ts === null) return null;
|
|
4567
|
+
return Math.floor((ts - now) / MS_PER_DAY);
|
|
4568
|
+
}
|
|
4569
|
+
function expiryMarker(days) {
|
|
4570
|
+
if (days === null) return "";
|
|
4571
|
+
if (days < 0) return `\t⚠ 만료됨`;
|
|
4572
|
+
if (days <= CERT_EXPIRY_WARN_DAYS) return `\t⚠ 만료 임박 (${days}일)`;
|
|
4573
|
+
return "";
|
|
4574
|
+
}
|
|
4299
4575
|
const certsLsCommand = defineCommand({
|
|
4300
4576
|
meta: {
|
|
4301
4577
|
name: "ls",
|
|
@@ -4330,27 +4606,153 @@ const certsLsCommand = defineCommand({
|
|
|
4330
4606
|
const { session, workspaceId } = ctx;
|
|
4331
4607
|
try {
|
|
4332
4608
|
const certs = await fetchCerts(workspaceId, appId, session.cookies);
|
|
4609
|
+
const now = Date.now();
|
|
4610
|
+
const augmented = certs.map((c) => ({
|
|
4611
|
+
...c,
|
|
4612
|
+
daysUntilExpiry: deriveDaysUntilExpiry(c, now)
|
|
4613
|
+
}));
|
|
4333
4614
|
if (args.json) {
|
|
4334
4615
|
emitJson({
|
|
4335
4616
|
ok: true,
|
|
4336
4617
|
workspaceId,
|
|
4337
4618
|
appId,
|
|
4338
|
-
certs
|
|
4619
|
+
certs: augmented
|
|
4339
4620
|
});
|
|
4340
4621
|
return exitAfterFlush(ExitCode.Ok);
|
|
4341
4622
|
}
|
|
4342
|
-
if (
|
|
4623
|
+
if (augmented.length === 0) {
|
|
4343
4624
|
process.stdout.write(`App ${appId} (ws ${workspaceId}): no mTLS certs\n`);
|
|
4344
4625
|
return exitAfterFlush(ExitCode.Ok);
|
|
4345
4626
|
}
|
|
4346
|
-
process.stdout.write(`App ${appId} (ws ${workspaceId}): ${
|
|
4347
|
-
for (const c of
|
|
4627
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): ${augmented.length} cert(s)\n`);
|
|
4628
|
+
for (const c of augmented) {
|
|
4348
4629
|
const id = typeof c.id === "string" || typeof c.id === "number" ? c.id : typeof c.certId === "string" || typeof c.certId === "number" ? c.certId : "-";
|
|
4349
4630
|
const cn = typeof c.commonName === "string" ? c.commonName : "-";
|
|
4350
4631
|
const createdAt = typeof c.createdAt === "string" ? c.createdAt : "";
|
|
4351
|
-
const expiresAt = typeof c.expiresAt === "string" ? c.expiresAt : typeof c.validUntil === "string" ? c.validUntil : "";
|
|
4352
|
-
process.stdout.write(`${id}\t${cn}\t${createdAt}\t${expiresAt}\n`);
|
|
4632
|
+
const expiresAt = typeof c.expiresAt === "string" ? c.expiresAt : typeof c.validUntil === "string" ? c.validUntil : typeof c.expireTs === "number" && Number.isFinite(c.expireTs) ? new Date(c.expireTs).toISOString() : "";
|
|
4633
|
+
process.stdout.write(`${id}\t${cn}\t${createdAt}\t${expiresAt}${expiryMarker(c.daysUntilExpiry)}\n`);
|
|
4634
|
+
}
|
|
4635
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
4636
|
+
} catch (err) {
|
|
4637
|
+
return emitFailureFromError(args.json, err);
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
});
|
|
4641
|
+
function pickCertById(certs, certId) {
|
|
4642
|
+
const target = certId.trim();
|
|
4643
|
+
if (target.length === 0) return null;
|
|
4644
|
+
for (const c of certs) {
|
|
4645
|
+
const candidate = typeof c.id === "string" || typeof c.id === "number" ? c.id : typeof c.certId === "string" || typeof c.certId === "number" ? c.certId : null;
|
|
4646
|
+
if (candidate !== null && String(candidate) === target) return c;
|
|
4647
|
+
}
|
|
4648
|
+
return null;
|
|
4649
|
+
}
|
|
4650
|
+
function augmentCertExpiry(cert, now = Date.now()) {
|
|
4651
|
+
let expiresAtMs;
|
|
4652
|
+
if (typeof cert.expireTs === "number" && Number.isFinite(cert.expireTs)) expiresAtMs = cert.expireTs;
|
|
4653
|
+
else if (typeof cert.expiresAt === "string") {
|
|
4654
|
+
const t = Date.parse(cert.expiresAt);
|
|
4655
|
+
if (Number.isFinite(t)) expiresAtMs = t;
|
|
4656
|
+
} else if (typeof cert.validUntil === "string") {
|
|
4657
|
+
const t = Date.parse(cert.validUntil);
|
|
4658
|
+
if (Number.isFinite(t)) expiresAtMs = t;
|
|
4659
|
+
}
|
|
4660
|
+
if (expiresAtMs === void 0) return {};
|
|
4661
|
+
const daysUntilExpiry = Math.floor((expiresAtMs - now) / 864e5);
|
|
4662
|
+
return {
|
|
4663
|
+
expiresAtMs,
|
|
4664
|
+
daysUntilExpiry
|
|
4665
|
+
};
|
|
4666
|
+
}
|
|
4667
|
+
const certsShowCommand = defineCommand({
|
|
4668
|
+
meta: {
|
|
4669
|
+
name: "show",
|
|
4670
|
+
description: "Show a single mTLS certificate by id (metadata only — no PEM)."
|
|
4671
|
+
},
|
|
4672
|
+
args: {
|
|
4673
|
+
certId: {
|
|
4674
|
+
type: "positional",
|
|
4675
|
+
description: "Cert ID (from `app certs ls`).",
|
|
4676
|
+
required: true
|
|
4677
|
+
},
|
|
4678
|
+
app: {
|
|
4679
|
+
type: "string",
|
|
4680
|
+
description: "Mini-app ID the cert belongs to."
|
|
4681
|
+
},
|
|
4682
|
+
workspace: {
|
|
4683
|
+
type: "string",
|
|
4684
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
4685
|
+
},
|
|
4686
|
+
json: {
|
|
4687
|
+
type: "boolean",
|
|
4688
|
+
description: "Emit machine-readable JSON.",
|
|
4689
|
+
default: false
|
|
4690
|
+
}
|
|
4691
|
+
},
|
|
4692
|
+
async run({ args }) {
|
|
4693
|
+
const certId = typeof args.certId === "string" ? args.certId.trim() : "";
|
|
4694
|
+
if (certId.length === 0) {
|
|
4695
|
+
if (args.json) emitJson({
|
|
4696
|
+
ok: false,
|
|
4697
|
+
reason: "missing-cert-id",
|
|
4698
|
+
message: "certId positional is required"
|
|
4699
|
+
});
|
|
4700
|
+
else process.stderr.write("app certs show: certId is required\n");
|
|
4701
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
4702
|
+
}
|
|
4703
|
+
const ctx = await resolveAppOrFail({
|
|
4704
|
+
json: args.json,
|
|
4705
|
+
appIdRaw: args.app,
|
|
4706
|
+
appIdField: "app",
|
|
4707
|
+
...args.workspace !== void 0 ? { workspace: args.workspace } : {}
|
|
4708
|
+
});
|
|
4709
|
+
if (!ctx) return;
|
|
4710
|
+
const appId = await requireMiniAppId(ctx, args.json);
|
|
4711
|
+
if (appId === null) return;
|
|
4712
|
+
printContextHeader(ctx, { json: args.json });
|
|
4713
|
+
const { session, workspaceId } = ctx;
|
|
4714
|
+
try {
|
|
4715
|
+
const match = pickCertById(await fetchCerts(workspaceId, appId, session.cookies), certId);
|
|
4716
|
+
if (match === null) {
|
|
4717
|
+
if (args.json) emitJson({
|
|
4718
|
+
ok: false,
|
|
4719
|
+
reason: "not-found",
|
|
4720
|
+
workspaceId,
|
|
4721
|
+
appId,
|
|
4722
|
+
certId,
|
|
4723
|
+
message: `cert ${certId} not found on app ${appId}`
|
|
4724
|
+
});
|
|
4725
|
+
else process.stderr.write(`app certs show: cert ${certId} not found on app ${appId} (ws ${workspaceId})\n`);
|
|
4726
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
4727
|
+
}
|
|
4728
|
+
const expiry = augmentCertExpiry(match);
|
|
4729
|
+
if (args.json) {
|
|
4730
|
+
emitJson({
|
|
4731
|
+
ok: true,
|
|
4732
|
+
workspaceId,
|
|
4733
|
+
appId,
|
|
4734
|
+
cert: match,
|
|
4735
|
+
...expiry
|
|
4736
|
+
});
|
|
4737
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
4738
|
+
}
|
|
4739
|
+
const id = typeof match.id === "string" || typeof match.id === "number" ? match.id : typeof match.certId === "string" || typeof match.certId === "number" ? match.certId : "-";
|
|
4740
|
+
const name = typeof match.name === "string" ? match.name : "-";
|
|
4741
|
+
const cn = typeof match.commonName === "string" ? match.commonName : "";
|
|
4742
|
+
const createdAt = typeof match.createdAt === "string" ? match.createdAt : "";
|
|
4743
|
+
const expiresAtIso = expiry.expiresAtMs !== void 0 ? new Date(expiry.expiresAtMs).toISOString() : "";
|
|
4744
|
+
const status = typeof match.status === "string" ? match.status : "";
|
|
4745
|
+
const lines = [`App ${appId} (ws ${workspaceId}): cert ${id}`, ` name: ${name}`];
|
|
4746
|
+
if (cn) lines.push(` commonName: ${cn}`);
|
|
4747
|
+
if (createdAt) lines.push(` createdAt: ${createdAt}`);
|
|
4748
|
+
if (expiresAtIso) lines.push(` expiresAt: ${expiresAtIso}`);
|
|
4749
|
+
if (expiry.daysUntilExpiry !== void 0) {
|
|
4750
|
+
const d = expiry.daysUntilExpiry;
|
|
4751
|
+
const suffix = d > 0 ? ` (D-${d})` : d === 0 ? " (expires today)" : ` (expired ${-d} day(s) ago)`;
|
|
4752
|
+
lines.push(` daysUntilExpiry: ${d}${suffix}`);
|
|
4353
4753
|
}
|
|
4754
|
+
if (status) lines.push(` status: ${status}`);
|
|
4755
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
4354
4756
|
return exitAfterFlush(ExitCode.Ok);
|
|
4355
4757
|
} catch (err) {
|
|
4356
4758
|
return emitFailureFromError(args.json, err);
|
|
@@ -4370,6 +4772,7 @@ const certsCommand = defineCommand({
|
|
|
4370
4772
|
},
|
|
4371
4773
|
subCommands: {
|
|
4372
4774
|
ls: certsLsCommand,
|
|
4775
|
+
show: certsShowCommand,
|
|
4373
4776
|
issue: defineCommand({
|
|
4374
4777
|
meta: {
|
|
4375
4778
|
name: "issue",
|
|
@@ -8498,7 +8901,7 @@ function resolveVersion() {
|
|
|
8498
8901
|
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
8499
8902
|
} catch {}
|
|
8500
8903
|
try {
|
|
8501
|
-
return "0.1.
|
|
8904
|
+
return "0.1.24";
|
|
8502
8905
|
} catch {}
|
|
8503
8906
|
return "0.0.0-dev";
|
|
8504
8907
|
}
|
|
@@ -8986,120 +9389,6 @@ const whoamiCommand = defineCommand({
|
|
|
8986
9389
|
}
|
|
8987
9390
|
});
|
|
8988
9391
|
//#endregion
|
|
8989
|
-
//#region src/api/workspaces.ts
|
|
8990
|
-
const WORKSPACES_BASE = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
8991
|
-
async function fetchWorkspaceDetail(workspaceId, cookies, opts = {}) {
|
|
8992
|
-
const raw = await requestConsoleApi({
|
|
8993
|
-
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}`,
|
|
8994
|
-
cookies,
|
|
8995
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
8996
|
-
});
|
|
8997
|
-
const id = raw.id;
|
|
8998
|
-
const name = raw.name;
|
|
8999
|
-
if (typeof id !== "number" || !Number.isInteger(id) || id <= 0 || typeof name !== "string") throw new Error(`Unexpected workspace detail shape for id=${workspaceId}`);
|
|
9000
|
-
const { id: _id, name: _name, ...extra } = raw;
|
|
9001
|
-
return {
|
|
9002
|
-
workspaceId: id,
|
|
9003
|
-
workspaceName: name,
|
|
9004
|
-
extra
|
|
9005
|
-
};
|
|
9006
|
-
}
|
|
9007
|
-
async function fetchWorkspacePartner(workspaceId, cookies, opts = {}) {
|
|
9008
|
-
const raw = await requestConsoleApi({
|
|
9009
|
-
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/partner`,
|
|
9010
|
-
cookies,
|
|
9011
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
9012
|
-
});
|
|
9013
|
-
const registered = raw.registered;
|
|
9014
|
-
if (typeof registered !== "boolean") throw new Error(`Unexpected workspace partner shape for id=${workspaceId}`);
|
|
9015
|
-
return {
|
|
9016
|
-
registered,
|
|
9017
|
-
approvalType: typeof raw.approvalType === "string" ? raw.approvalType : null,
|
|
9018
|
-
rejectMessage: typeof raw.rejectMessage === "string" ? raw.rejectMessage : null,
|
|
9019
|
-
partner: raw.partner && typeof raw.partner === "object" ? raw.partner : null
|
|
9020
|
-
};
|
|
9021
|
-
}
|
|
9022
|
-
const WORKSPACE_TERM_TYPES = [
|
|
9023
|
-
"TOSS_LOGIN",
|
|
9024
|
-
"BIZ_WORKSPACE",
|
|
9025
|
-
"TOSS_PROMOTION_MONEY",
|
|
9026
|
-
"IAA",
|
|
9027
|
-
"IAP"
|
|
9028
|
-
];
|
|
9029
|
-
const DEFAULT_SEGMENT_CATEGORY = "생성된 세그먼트";
|
|
9030
|
-
async function fetchWorkspaceSegments(params, cookies, opts = {}) {
|
|
9031
|
-
const page = params.page ?? 0;
|
|
9032
|
-
const qs = new URLSearchParams();
|
|
9033
|
-
qs.set("category", params.category ?? DEFAULT_SEGMENT_CATEGORY);
|
|
9034
|
-
qs.set("search", params.search ?? "");
|
|
9035
|
-
qs.set("page", String(page));
|
|
9036
|
-
const raw = await requestConsoleApi({
|
|
9037
|
-
url: `${WORKSPACES_BASE}/workspaces/${params.workspaceId}/segments/list?${qs.toString()}`,
|
|
9038
|
-
cookies,
|
|
9039
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
9040
|
-
});
|
|
9041
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected segments shape for workspace=${params.workspaceId}`);
|
|
9042
|
-
const data = raw;
|
|
9043
|
-
return {
|
|
9044
|
-
contents: (Array.isArray(data.contents) ? data.contents : []).map((c) => c && typeof c === "object" ? c : {}),
|
|
9045
|
-
totalPage: typeof data.totalPage === "number" ? data.totalPage : 0,
|
|
9046
|
-
currentPage: typeof data.currentPage === "number" ? data.currentPage : page
|
|
9047
|
-
};
|
|
9048
|
-
}
|
|
9049
|
-
async function fetchWorkspaceTerms(workspaceId, type, cookies, opts = {}) {
|
|
9050
|
-
const raw = await requestConsoleApi({
|
|
9051
|
-
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms/${type}/skip-permission`,
|
|
9052
|
-
cookies,
|
|
9053
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
9054
|
-
});
|
|
9055
|
-
if (!Array.isArray(raw)) throw new Error(`Unexpected workspace terms shape for type=${type}`);
|
|
9056
|
-
return raw.map((entry, i) => {
|
|
9057
|
-
if (!entry || typeof entry !== "object") throw new Error(`Unexpected workspace terms entry at index ${i} for type=${type}`);
|
|
9058
|
-
const e = entry;
|
|
9059
|
-
return {
|
|
9060
|
-
required: Boolean(e.required),
|
|
9061
|
-
termsId: typeof e.termsId === "number" ? e.termsId : 0,
|
|
9062
|
-
revisionId: typeof e.revisionId === "number" ? e.revisionId : 0,
|
|
9063
|
-
title: typeof e.title === "string" ? e.title : "",
|
|
9064
|
-
contentsUrl: typeof e.contentsUrl === "string" ? e.contentsUrl : "",
|
|
9065
|
-
actionType: typeof e.actionType === "string" ? e.actionType : "",
|
|
9066
|
-
isAgreed: Boolean(e.isAgreed),
|
|
9067
|
-
isOneTimeConsent: Boolean(e.isOneTimeConsent)
|
|
9068
|
-
};
|
|
9069
|
-
});
|
|
9070
|
-
}
|
|
9071
|
-
/**
|
|
9072
|
-
* Persist agreement for one-or-more workspace terms. The endpoint takes a
|
|
9073
|
-
* single `agreedList` regardless of which bucket the terms came from — the
|
|
9074
|
-
* type tag is implicit in the (termsId, revisionId) pairs.
|
|
9075
|
-
*
|
|
9076
|
-
* Captured behaviour (2026-05-08, ws=36577):
|
|
9077
|
-
* - `POST /workspaces/<wid>/console-workspace-terms` with body
|
|
9078
|
-
* `{"agreedList":[{"termsId": <int>, "revisionId": <int>}, ...]}`
|
|
9079
|
-
* - Response on success: `{"resultType":"SUCCESS","success":{}}` — no
|
|
9080
|
-
* useful payload. We resolve `void`.
|
|
9081
|
-
* - Re-submitting an already-agreed term returns `errorCode: 500`
|
|
9082
|
-
* (Internal Server Error). The server is NOT idempotent, so callers
|
|
9083
|
-
* must filter to `isAgreed === false` before invoking.
|
|
9084
|
-
* - Empty `agreedList` returns SUCCESS (no-op), but we throw client-side
|
|
9085
|
-
* before sending — round-tripping a no-op request is wasted.
|
|
9086
|
-
*
|
|
9087
|
-
* Failure surfaces through `TossApiError` like every other write helper.
|
|
9088
|
-
*/
|
|
9089
|
-
async function agreeWorkspaceTerms(workspaceId, terms, cookies, opts = {}) {
|
|
9090
|
-
if (terms.length === 0) throw new Error("agreeWorkspaceTerms requires at least one term");
|
|
9091
|
-
await requestConsoleApi({
|
|
9092
|
-
method: "POST",
|
|
9093
|
-
url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms`,
|
|
9094
|
-
cookies,
|
|
9095
|
-
body: { agreedList: terms.map(({ termsId, revisionId }) => ({
|
|
9096
|
-
termsId,
|
|
9097
|
-
revisionId
|
|
9098
|
-
})) },
|
|
9099
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
9100
|
-
});
|
|
9101
|
-
}
|
|
9102
|
-
//#endregion
|
|
9103
9392
|
//#region src/commands/workspace.ts
|
|
9104
9393
|
function formatScalar(v) {
|
|
9105
9394
|
if (v === null) return "null";
|