@ekanos/cli 0.17.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/commands/add.js +1 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/catalog.js +11 -7
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/names.d.ts +19 -0
- package/dist/commands/names.js +122 -0
- package/dist/commands/names.js.map +1 -0
- package/dist/commands/publish.d.ts +53 -3
- package/dist/commands/publish.js +58 -16
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/sources.js +15 -5
- package/dist/commands/sources.js.map +1 -1
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +12 -12
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.js +2 -2
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/teams-connect.js +1 -1
- package/dist/commands/teams-connect.js.map +1 -1
- package/dist/experience/boundary.d.ts +2 -2
- package/dist/experience/boundary.js +2 -11
- package/dist/experience/boundary.js.map +1 -1
- package/dist/experience/build.d.ts +3 -1
- package/dist/experience/build.js +15 -26
- package/dist/experience/build.js.map +1 -1
- package/dist/experience/clean-room.d.ts +40 -0
- package/dist/experience/clean-room.js +150 -0
- package/dist/experience/clean-room.js.map +1 -0
- package/dist/experience/contract-sources.d.ts +7 -2
- package/dist/experience/contract-sources.js +15 -8
- package/dist/experience/contract-sources.js.map +1 -1
- package/dist/experience/definition-checks.d.ts +12 -1
- package/dist/experience/definition-checks.js +34 -11
- package/dist/experience/definition-checks.js.map +1 -1
- package/dist/experience/definition-shape.d.ts +20 -2
- package/dist/experience/definition-shape.js.map +1 -1
- package/dist/experience/project.js +8 -2
- package/dist/experience/project.js.map +1 -1
- package/dist/experience/publish.d.ts +42 -5
- package/dist/experience/publish.js +195 -11
- package/dist/experience/publish.js.map +1 -1
- package/dist/harness-scaffold.js +3 -1
- package/dist/harness-scaffold.js.map +1 -1
- package/dist/index.js +25 -2
- package/dist/index.js.map +1 -1
- package/dist/names-api.d.ts +90 -0
- package/dist/names-api.js +123 -0
- package/dist/names-api.js.map +1 -0
- package/dist/product-kinds.d.ts +13 -0
- package/dist/product-kinds.js +17 -0
- package/dist/product-kinds.js.map +1 -0
- package/dist/publish-api.d.ts +38 -5
- package/dist/publish-api.js +59 -16
- package/dist/publish-api.js.map +1 -1
- package/dist/repo/publish.d.ts +6 -3
- package/dist/repo/publish.js +48 -14
- package/dist/repo/publish.js.map +1 -1
- package/dist/source-products-api.d.ts +3 -2
- package/dist/source-products-api.js +1 -1
- package/dist/source-products-api.js.map +1 -1
- package/dist/team-connect-api.d.ts +4 -0
- package/dist/team-connect-api.js +1 -1
- package/dist/team-connect-api.js.map +1 -1
- package/dist/toolchain-api.d.ts +15 -0
- package/dist/toolchain-api.js +12 -1
- package/dist/toolchain-api.js.map +1 -1
- package/package.json +2 -2
- package/templates/experience/package.json.tmpl +7 -4
- package/templates/harness/app/experience/layout/page.tsx.tmpl +1 -0
- package/templates/harness/app/experience/layout.tsx.tmpl +3 -0
- package/templates/harness/app/experience/pages/[id]/[...path]/page.tsx.tmpl +1 -0
- package/templates/package.json.tmpl +4 -4
package/dist/publish-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-api.js","sourceRoot":"","sources":["../src/publish-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { request } from './auth/ekanos-api';\nimport { loginHint } from './auth/host-trust';\nimport type { AuthEnvironment } from './auth/session';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n validationError,\n} from './errors';\n\n/**\n * The CLI's client for the Ekanos submission intake route.\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the seam between this package and the Ekanos web app.\n *\n * The server side is the host's `/api/partner/submissions` route. As with\n * the auth routes (see `auth/ekanos-api.ts`), the two halves are coupled\n * only by HTTP; tests stub `fetch`, never the route.\n *\n * POST /api/partner/submissions [Authorization: Bearer <access_token>]\n * ← multipart/form-data:\n * source — Ekanos source slug\n * slug — integration slug (kebab-case)\n * version — semver\n * manifest — JSON string (the parsed ekanos.json + declaration summary)\n * archive — gzipped tarball, ≤ 4 MiB\n * → 201 { ok: true, data: { submissionId, slug, version, state,\n * archiveSha256 } }\n * → 400 { error, code: \"INVALID_REQUEST\" | \"INVALID_ARCHIVE\" }\n * → 401 { error, code: \"AUTH_REQUIRED\" } token absent or expired —\n * refresh once and retry\n * → 403 { error, code: \"FORBIDDEN\" } no developer seat on the source\n * → 404 { error, code: \"SOURCE_NOT_FOUND\" }\n * → 409 { error, code: \"VERSION_EXISTS\" } this (slug, version) is\n * already submitted\n * → 413 { error, code: \"ARCHIVE_TOO_LARGE\" }\n * → 3xx treated as 401 (an\n * auth-gated Ekanos page\n * answers 307)\n *\n * GET /api/partner/submissions?source=<slug|id>[&detail=findings]\n * [Authorization: Bearer]\n * → 200 { ok: true, data: { source, integrations: [{ id, slug, name,\n * createdAt, versions: [{ id, version, state, archiveSha256,\n * submittedAt, gatedAt,\n * // present once a gate has run (gate_report is non-null):\n * errorCount?, warnCount?,\n * // present ONLY with ?detail=findings, alongside the above:\n * findings?, stages?\n * }] }] } }\n * → 400 { error, code: \"INVALID_REQUEST\" }\n * → 401 { error, code: \"AUTH_REQUIRED\" } refresh once and retry\n * → 403 { error, code: \"MFA_REQUIRED\" }\n * → 404 { error, code: \"SOURCE_NOT_FOUND\" }\n * → 3xx treated as 401\n *\n * `state` walks submitted -> gated|rejected -> dev -> prod -> revoked,\n * enforced by a database trigger on the host's side (the full transition\n * matrix lives there). `gated`/`dev`/`rejected` are the\n * terminal-for-waiting states `ekanos status --watch` polls for.\n * ---------------------------------------------------------------------------\n */\n\n/**\n * The server's manifest cap — 64 KiB of UTF-8, enforced server-side against\n * `Buffer.byteLength`. Mirrored here so `publish` can refuse an oversized\n * manifest with a clear message instead of the server's generic 400.\n */\nexport const MAX_MANIFEST_BYTES = 64 * 1024;\n\nexport interface SubmissionPayload {\n source: string;\n slug: string;\n version: string;\n /** JSON string — already serialized by the caller. */\n manifest: string;\n archive: Uint8Array;\n}\n\nexport interface SubmissionReceipt {\n submissionId: string;\n slug: string;\n version: string;\n state: string;\n archiveSha256: string;\n}\n\nexport type SubmitResult =\n | { status: 'ok'; receipt: SubmissionReceipt }\n /** The bearer token did not authenticate — refresh and retry, once. */\n | { status: 'unauthorized' };\n\nexport async function submitArchive(\n host: string,\n accessToken: string,\n payload: SubmissionPayload,\n): Promise<SubmitResult> {\n const form = new FormData();\n form.append('source', payload.source);\n form.append('slug', payload.slug);\n form.append('version', payload.version);\n form.append('manifest', payload.manifest);\n form.append(\n 'archive',\n // Copy into a fresh Uint8Array: Buffer is Uint8Array<ArrayBufferLike>,\n // which BlobPart refuses (a SharedArrayBuffer view is not a BlobPart).\n new Blob([new Uint8Array(payload.archive)], { type: 'application/gzip' }),\n 'archive.tgz',\n );\n\n const response = await request(`${host}/api/partner/submissions`, {\n method: 'POST',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n body: form,\n });\n\n // 401, or the 307 an auth-gated Ekanos route answers with: the token did\n // not authenticate. The caller owns the refresh-and-retry decision.\n if (response.status === 401 || isRedirect(response.status)) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const serverMessage = str(body.error);\n\n if (response.status === 403) {\n throw forbiddenError(\n serverMessage ??\n `${host} refused the submission: this account does not hold the ` +\n `developer seat on source \"${payload.source}\".`,\n `Ask the source operator to grant your account the developer seat for ` +\n `\"${payload.source}\", then retry.`,\n );\n }\n\n if (response.status === 404) {\n throw notFoundError(\n serverMessage ?? `${host} has no source with slug \"${payload.source}\".`,\n `Check the --source value (or the \"source\" field in ekanos.json) ` +\n `against the slug your Ekanos operator gave you.`,\n );\n }\n\n if (response.status === 409) {\n throw invalidStateError(\n serverMessage ??\n `Version ${payload.version} of \"${payload.slug}\" is already submitted.`,\n `Submitted versions are immutable. Bump the \"version\" field in ` +\n `package.json and publish again.`,\n );\n }\n\n if (response.status === 400 || response.status === 413) {\n throw validationError(\n serverMessage ?? `${host} rejected the submission as invalid.`,\n `Fix the reported problem and re-run \"ekanos publish\". If the message ` +\n `names the archive, re-check the project layout (\"ekanos validate\").`,\n );\n }\n\n if (!response.ok) {\n throw networkError(\n `${host}/api/partner/submissions responded ${response.status}.`,\n response.status >= 500\n ? `The Ekanos deployment returned a server error. Retry shortly; if ` +\n `it persists, report it with the status code.`\n : `Confirm the host is an Ekanos deployment running a build that ` +\n `includes the partner submissions route.`,\n );\n }\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const receipt = {\n submissionId: str(data.submissionId),\n slug: str(data.slug),\n version: str(data.version),\n state: str(data.state),\n archiveSha256: str(data.archiveSha256),\n };\n\n if (Object.values(receipt).some((value) => value === null)) {\n throw networkError(\n `${host} accepted the submission but returned an incomplete receipt.`,\n `Confirm the deployment is running an Ekanos build that includes the ` +\n `partner submissions route, then check the submission's state with ` +\n `your operator before re-publishing.`,\n );\n }\n\n return { status: 'ok', receipt: receipt as SubmissionReceipt };\n}\n\n/**\n * One gate finding, mirroring `@ekanos/integration-schema`'s `Finding` shape\n * (the same one `validate`/`publish` carry in `data.findings`). Declared\n * locally rather than imported: the gate's own report type lives in a\n * server-side toolchain package private to the platform monorepo, which this\n * partner-facing CLI has no business depending on — the wire shape is the\n * only contract.\n *\n * `origin`/`id`/`createdAt`/`resolvedAt` are the additive fields the host's\n * findings-merge endpoint puts on the wire once host-filed findings — CI\n * failures, host observations, a revocation reason — are merged in alongside\n * the gate's own verdict. A gate-only finding still omits them entirely\n * (this CLI's own `validate`/`publish` findings never set `origin`, which\n * the renderer reads as \"gate\"); they only appear on findings read back from\n * `GET /api/partner/submissions?detail=findings`.\n */\nexport interface GateFinding {\n check: string;\n severity: 'error' | 'warn' | 'info';\n file?: string;\n line?: number;\n message: string;\n hint: string;\n /** `'gate' | 'ci' | 'host' | 'revocation'` on the wire; kept as `string` — see the doc comment on `HostFindingRow` in `merge-findings.ts` for why the host stays defensive rather than a narrower literal union here. */\n origin?: string;\n id?: string;\n createdAt?: string;\n resolvedAt?: string | null;\n}\n\nexport interface GateStageResult {\n id: string;\n status: 'pass' | 'fail' | 'skipped';\n durationMs: number;\n}\n\nexport interface SubmissionVersion {\n id: string;\n version: string;\n state: string;\n archiveSha256: string | null;\n submittedAt: string | null;\n gatedAt: string | null;\n /** Present once a gate has run. */\n errorCount: number | null;\n warnCount: number | null;\n /** Present only when the request passed `?detail=findings`. */\n findings?: GateFinding[];\n stages?: GateStageResult[];\n}\n\nexport interface SubmissionListing {\n id: string;\n slug: string;\n name: string | null;\n createdAt: string | null;\n versions: SubmissionVersion[];\n}\n\nexport type ListResult =\n | { status: 'ok'; integrations: SubmissionListing[] }\n /** The bearer token did not authenticate — refresh and retry, once. */\n | { status: 'unauthorized' };\n\n/**\n * List the caller's visible submissions for a source. Same authorization model\n * as `submitArchive`: the seat SELECT policies filter server-side, so a caller\n * with no seat sees an empty list rather than an error.\n *\n * `detail: 'findings'` requests the full per-version findings/stages arrays\n * (used by `status --findings` and `status --watch`); omitted, the server\n * answers with counts only — see the wire contract above.\n */\nexport async function listSubmissions(\n host: string,\n accessToken: string,\n source: string,\n options: { detail?: 'findings' } = {},\n): Promise<ListResult> {\n const query = new URLSearchParams({ source });\n if (options.detail) query.set('detail', options.detail);\n\n const response = await request(\n `${host}/api/partner/submissions?${query.toString()}`,\n {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n },\n );\n\n if (response.status === 401 || isRedirect(response.status)) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const serverMessage = str(body.error);\n\n if (response.status === 403) {\n throw forbiddenError(\n serverMessage ?? `${host} refused to list submissions for \"${source}\".`,\n `Complete MFA in the browser if the message names it, then run ` +\n `\"ekanos login\" again.`,\n );\n }\n\n if (response.status === 404) {\n throw notFoundError(\n serverMessage ?? `${host} has no source with slug \"${source}\".`,\n `Check the \"source\" field in ekanos.json (or --source) against the ` +\n `slug your Ekanos operator gave you.`,\n );\n }\n\n if (response.status === 400) {\n throw validationError(\n serverMessage ?? `${host} rejected the submissions query.`,\n `Fix the reported problem and re-run \"ekanos status\".`,\n );\n }\n\n if (!response.ok) {\n throw networkError(\n `${host}/api/partner/submissions responded ${response.status}.`,\n response.status >= 500\n ? `The Ekanos deployment returned a server error. Retry shortly; if ` +\n `it persists, report it with the status code.`\n : `Confirm the host is an Ekanos deployment running a build that ` +\n `includes the partner submissions route.`,\n );\n }\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const rows = Array.isArray(data.integrations) ? data.integrations : [];\n\n const integrations = rows\n .filter(\n (row): row is Record<string, unknown> =>\n typeof row === 'object' && row !== null,\n )\n .map((row) => ({\n id: str(row.id) ?? '',\n slug: str(row.slug) ?? '',\n name: str(row.name),\n createdAt: str(row.createdAt),\n versions: (Array.isArray(row.versions) ? row.versions : [])\n .filter(\n (version): version is Record<string, unknown> =>\n typeof version === 'object' && version !== null,\n )\n .map((version) => ({\n id: str(version.id) ?? '',\n version: str(version.version) ?? '',\n state: str(version.state) ?? '',\n archiveSha256: str(version.archiveSha256),\n submittedAt: str(version.submittedAt),\n gatedAt: str(version.gatedAt),\n errorCount: num(version.errorCount),\n warnCount: num(version.warnCount),\n ...(Array.isArray(version.findings)\n ? { findings: version.findings as GateFinding[] }\n : {}),\n ...(Array.isArray(version.stages)\n ? { stages: version.stages as GateStageResult[] }\n : {}),\n })),\n }));\n\n return { status: 'ok', integrations };\n}\n\n/** Guard for the \"still unauthorized after one refresh\" terminal case. */\nexport function stillUnauthorized(env: AuthEnvironment): never {\n throw authRequiredError(\n `The session for ${env.host} could not authorize the submission.`,\n loginHint(env),\n );\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction isRedirect(status: number): boolean {\n return status >= 300 && status < 400;\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nfunction num(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"publish-api.js","sourceRoot":"","sources":["../src/publish-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { request } from './auth/ekanos-api';\nimport { loginHint } from './auth/host-trust';\nimport type { AuthEnvironment } from './auth/session';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n validationError,\n} from './errors';\n\n/**\n * The CLI's client for the Ekanos submission intake route.\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the seam between this package and the Ekanos web app.\n *\n * The server side is the host's `/api/partner/submissions` route. As with\n * the auth routes (see `auth/ekanos-api.ts`), the two halves are coupled\n * only by HTTP; tests stub `fetch`, never the route.\n *\n * POST /api/partner/submissions [Authorization: Bearer <access_token>]\n * ← multipart/form-data:\n * source — Ekanos source slug\n * slug — integration (or experience) slug (kebab-case; bare, never namespaced)\n * version — semver\n * kind — \"experience\" for a composed experience; omitted for an\n * integration (an older host never sees the field). Sent\n * only to a host that advertises\n * `capabilities.experienceSubmissions` on the toolchain\n * route.\n * manifest — JSON string (the parsed ekanos.json + declaration\n * summary; for an experience, its built manifest.json)\n * archive — gzipped tarball, ≤ 4 MiB\n * availability — \"global\" | \"restricted\"; sent only when ekanos.json\n * declares it\n * → 201 { ok: true, data: { submissionId, slug, version, state,\n * archiveSha256,\n * // newer hosts only:\n * namespace?: string|null, displayName? } }\n * → 400 { error, code: \"INVALID_REQUEST\" | \"INVALID_ARCHIVE\" }\n * → 401 { error, code: \"AUTH_REQUIRED\" } token absent or expired —\n * refresh once and retry\n * → 403 { error, code: \"FORBIDDEN\" } no developer seat on the source\n * → 404 { error, code: \"SOURCE_NOT_FOUND\" }\n * → 409 { error, code: \"VERSION_EXISTS\" } this (slug, version) is\n * already submitted\n * → 409 { error, code: \"KIND_MISMATCH\" } the slug is registered as\n * the other kind\n * → 409 { error, code: \"GLOBAL_NAME_UNAPPROVED\", hint? }\n * availability \"global\" without\n * an approved global name\n * → 409 { error, code: \"NAMESPACE_REQUIRED\" } the source has no namespace\n * → 413 { error, code: \"ARCHIVE_TOO_LARGE\" }\n * → 3xx treated as 401 (an\n * auth-gated Ekanos page\n * answers 307)\n *\n * GET /api/partner/submissions?source=<slug|id>[&detail=findings]\n * [Authorization: Bearer]\n * → 200 { ok: true, data: { source, integrations: [{ id, slug, name,\n * createdAt, namespace?: string|null, displayName?, versions: [{ id, version, state, archiveSha256,\n * submittedAt, gatedAt,\n * // present once a gate has run (gate_report is non-null):\n * errorCount?, warnCount?,\n * // present ONLY with ?detail=findings, alongside the above:\n * findings?, stages?\n * }] }] } }\n * → 400 { error, code: \"INVALID_REQUEST\" }\n * → 401 { error, code: \"AUTH_REQUIRED\" } refresh once and retry\n * → 403 { error, code: \"MFA_REQUIRED\" }\n * → 404 { error, code: \"SOURCE_NOT_FOUND\" }\n * → 3xx treated as 401\n *\n * `state` walks submitted -> gated|rejected -> dev -> prod -> revoked (a\n * `dev` version is marked `superseded` once a newer version reaches dev),\n * enforced by a database trigger on the host's side (the full transition\n * matrix lives there). `gated`/`dev`/`rejected` are the\n * terminal-for-waiting states `ekanos status --watch` polls for.\n * ---------------------------------------------------------------------------\n */\n\n/**\n * The server's manifest cap — 64 KiB of UTF-8, enforced server-side against\n * `Buffer.byteLength`. Mirrored here so `publish` can refuse an oversized\n * manifest with a clear message instead of the server's generic 400.\n */\nexport const MAX_MANIFEST_BYTES = 64 * 1024;\n\nexport interface SubmissionPayload {\n source: string;\n slug: string;\n /** Omitted for an integration. */\n kind?: 'experience';\n version: string;\n /** JSON string — already serialized by the caller. */\n manifest: string;\n archive: Uint8Array;\n /** Sent only when ekanos.json declares an availability mode. */\n availability?: 'global' | 'restricted';\n}\n\nexport interface SubmissionReceipt {\n submissionId: string;\n slug: string;\n version: string;\n state: string;\n archiveSha256: string;\n /** The Source namespace the package is named under; absent on an older host. */\n namespace?: string | null;\n /** The full name the host uses (`acme/google-drive`); absent on an older host. */\n displayName?: string;\n}\n\nexport type SubmitResult =\n | { status: 'ok'; receipt: SubmissionReceipt }\n /** The bearer token did not authenticate — refresh and retry, once. */\n | { status: 'unauthorized' };\n\nexport async function submitArchive(\n host: string,\n accessToken: string,\n payload: SubmissionPayload,\n): Promise<SubmitResult> {\n const form = new FormData();\n form.append('source', payload.source);\n form.append('slug', payload.slug);\n form.append('version', payload.version);\n if (payload.kind) form.append('kind', payload.kind);\n form.append('manifest', payload.manifest);\n if (payload.availability) form.append('availability', payload.availability);\n form.append(\n 'archive',\n // Copy into a fresh Uint8Array: Buffer is Uint8Array<ArrayBufferLike>,\n // which BlobPart refuses (a SharedArrayBuffer view is not a BlobPart).\n new Blob([new Uint8Array(payload.archive)], { type: 'application/gzip' }),\n 'archive.tgz',\n );\n\n const response = await request(`${host}/api/partner/submissions`, {\n method: 'POST',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n body: form,\n });\n\n // 401, or the 307 an auth-gated Ekanos route answers with: the token did\n // not authenticate. The caller owns the refresh-and-retry decision.\n if (response.status === 401 || isRedirect(response.status)) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const serverMessage = str(body.error);\n\n if (response.status === 403) {\n throw forbiddenError(\n serverMessage ??\n `${host} refused the submission: this account does not hold the ` +\n `developer seat on source \"${payload.source}\".`,\n `Ask the source operator to grant your account the developer seat for ` +\n `\"${payload.source}\", then retry.`,\n );\n }\n\n if (response.status === 404) {\n throw notFoundError(\n serverMessage ?? `${host} has no source with slug \"${payload.source}\".`,\n `Check the --source value (or the \"source\" field in ekanos.json) ` +\n `against the slug your Ekanos operator gave you.`,\n );\n }\n\n if (response.status === 409 && str(body.code) === 'KIND_MISMATCH') {\n throw invalidStateError(\n serverMessage ??\n `\"${payload.slug}\" is registered as the other kind of package.`,\n `A slug names one kind for its whole life. Pick a different slug, or ` +\n `submit it as the kind it was first registered as.`,\n );\n }\n\n if (response.status === 409 && body.code === 'GLOBAL_NAME_UNAPPROVED') {\n throw globalNameUnapprovedError(payload.slug);\n }\n\n if (response.status === 409 && body.code === 'NAMESPACE_REQUIRED') {\n throw invalidStateError(\n serverMessage ??\n `Source \"${payload.source}\" has no namespace to name \"${payload.slug}\" under.`,\n str(body.hint) ??\n `Ask an Ekanos super-admin to give \"${payload.source}\" a namespace, ` +\n `then retry.`,\n );\n }\n\n if (response.status === 409) {\n throw invalidStateError(\n serverMessage ??\n `Version ${payload.version} of \"${payload.slug}\" is already submitted.`,\n `Submitted versions are immutable. Bump the \"version\" field in ` +\n `package.json and publish again.`,\n );\n }\n\n if (response.status === 400 || response.status === 413) {\n throw validationError(\n serverMessage ?? `${host} rejected the submission as invalid.`,\n `Fix the reported problem and re-run \"ekanos publish\". If the message ` +\n `names the archive, re-check the project layout (\"ekanos validate\").`,\n );\n }\n\n if (!response.ok) {\n throw networkError(\n `${host}/api/partner/submissions responded ${response.status}.`,\n response.status >= 500\n ? `The Ekanos deployment returned a server error. Retry shortly; if ` +\n `it persists, report it with the status code.`\n : `Confirm the host is an Ekanos deployment running a build that ` +\n `includes the partner submissions route.`,\n );\n }\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const receipt = {\n submissionId: str(data.submissionId),\n slug: str(data.slug),\n version: str(data.version),\n state: str(data.state),\n archiveSha256: str(data.archiveSha256),\n };\n const displayName = str(data.displayName);\n\n if (Object.values(receipt).some((value) => value === null)) {\n throw networkError(\n `${host} accepted the submission but returned an incomplete receipt.`,\n `Confirm the deployment is running an Ekanos build that includes the ` +\n `partner submissions route, then check the submission's state with ` +\n `your operator before re-publishing.`,\n );\n }\n\n return {\n status: 'ok',\n receipt: {\n ...(receipt as SubmissionReceipt),\n ...(data.namespace === null || typeof data.namespace === 'string'\n ? { namespace: data.namespace }\n : {}),\n ...(displayName ? { displayName } : {}),\n },\n };\n}\n\n/**\n * The refusal for a `global` package whose name no super-admin has approved.\n * Shared by the pre-flight (`publish.ts`) and the 409 the host answers with.\n */\nexport function globalNameUnapprovedError(slug: string) {\n return invalidStateError(\n `The global name \"${slug}\" is not approved.`,\n `Run \"ekanos names claim ${slug}\" to request it, or publish restricted ` +\n `(remove availability.mode \"global\" from ekanos.json, or set it to ` +\n `\"restricted\").`,\n );\n}\n\n/**\n * One gate finding, mirroring `@ekanos/integration-schema`'s `Finding` shape\n * (the same one `validate`/`publish` carry in `data.findings`). Declared\n * locally rather than imported: the gate's own report type lives in a\n * server-side toolchain package private to the platform monorepo, which this\n * partner-facing CLI has no business depending on — the wire shape is the\n * only contract.\n *\n * `origin`/`id`/`createdAt`/`resolvedAt` are the additive fields the host's\n * findings-merge endpoint puts on the wire once host-filed findings — CI\n * failures, host observations, a revocation reason — are merged in alongside\n * the gate's own verdict. A gate-only finding still omits them entirely\n * (this CLI's own `validate`/`publish` findings never set `origin`, which\n * the renderer reads as \"gate\"); they only appear on findings read back from\n * `GET /api/partner/submissions?detail=findings`.\n */\nexport interface GateFinding {\n check: string;\n severity: 'error' | 'warn' | 'info';\n file?: string;\n line?: number;\n message: string;\n hint: string;\n /** `'gate' | 'ci' | 'host' | 'revocation'` on the wire; kept as `string` — see the doc comment on `HostFindingRow` in `merge-findings.ts` for why the host stays defensive rather than a narrower literal union here. */\n origin?: string;\n id?: string;\n createdAt?: string;\n resolvedAt?: string | null;\n}\n\nexport interface GateStageResult {\n id: string;\n status: 'pass' | 'fail' | 'skipped';\n durationMs: number;\n}\n\nexport interface SubmissionVersion {\n id: string;\n version: string;\n state: string;\n archiveSha256: string | null;\n submittedAt: string | null;\n gatedAt: string | null;\n /** Present once a gate has run. */\n errorCount: number | null;\n warnCount: number | null;\n /** Present only when the request passed `?detail=findings`. */\n findings?: GateFinding[];\n stages?: GateStageResult[];\n}\n\nexport interface SubmissionListing {\n id: string;\n slug: string;\n name: string | null;\n createdAt: string | null;\n /** Absent on an older host. */\n namespace?: string | null;\n displayName?: string;\n versions: SubmissionVersion[];\n}\n\nexport type ListResult =\n | { status: 'ok'; integrations: SubmissionListing[] }\n /** The bearer token did not authenticate — refresh and retry, once. */\n | { status: 'unauthorized' };\n\n/**\n * List the caller's visible submissions for a source. Same authorization model\n * as `submitArchive`: the seat SELECT policies filter server-side, so a caller\n * with no seat sees an empty list rather than an error.\n *\n * `detail: 'findings'` requests the full per-version findings/stages arrays\n * (used by `status --findings` and `status --watch`); omitted, the server\n * answers with counts only — see the wire contract above.\n */\nexport async function listSubmissions(\n host: string,\n accessToken: string,\n source: string,\n options: { detail?: 'findings' } = {},\n): Promise<ListResult> {\n const query = new URLSearchParams({ source });\n if (options.detail) query.set('detail', options.detail);\n\n const response = await request(\n `${host}/api/partner/submissions?${query.toString()}`,\n {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n },\n );\n\n if (response.status === 401 || isRedirect(response.status)) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const serverMessage = str(body.error);\n\n if (response.status === 403) {\n throw forbiddenError(\n serverMessage ?? `${host} refused to list submissions for \"${source}\".`,\n `Complete MFA in the browser if the message names it, then run ` +\n `\"ekanos login\" again.`,\n );\n }\n\n if (response.status === 404) {\n throw notFoundError(\n serverMessage ?? `${host} has no source with slug \"${source}\".`,\n `Check the \"source\" field in ekanos.json (or --source) against the ` +\n `slug your Ekanos operator gave you.`,\n );\n }\n\n if (response.status === 400) {\n throw validationError(\n serverMessage ?? `${host} rejected the submissions query.`,\n `Fix the reported problem and re-run \"ekanos status\".`,\n );\n }\n\n if (!response.ok) {\n throw networkError(\n `${host}/api/partner/submissions responded ${response.status}.`,\n response.status >= 500\n ? `The Ekanos deployment returned a server error. Retry shortly; if ` +\n `it persists, report it with the status code.`\n : `Confirm the host is an Ekanos deployment running a build that ` +\n `includes the partner submissions route.`,\n );\n }\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const rows = Array.isArray(data.integrations) ? data.integrations : [];\n\n const integrations = rows\n .filter(\n (row): row is Record<string, unknown> =>\n typeof row === 'object' && row !== null,\n )\n .map((row) => ({\n id: str(row.id) ?? '',\n slug: str(row.slug) ?? '',\n name: str(row.name),\n createdAt: str(row.createdAt),\n ...(row.namespace === null || typeof row.namespace === 'string'\n ? { namespace: row.namespace }\n : {}),\n ...(str(row.displayName) ? { displayName: str(row.displayName)! } : {}),\n versions: (Array.isArray(row.versions) ? row.versions : [])\n .filter(\n (version): version is Record<string, unknown> =>\n typeof version === 'object' && version !== null,\n )\n .map((version) => ({\n id: str(version.id) ?? '',\n version: str(version.version) ?? '',\n state: str(version.state) ?? '',\n archiveSha256: str(version.archiveSha256),\n submittedAt: str(version.submittedAt),\n gatedAt: str(version.gatedAt),\n errorCount: num(version.errorCount),\n warnCount: num(version.warnCount),\n ...(Array.isArray(version.findings)\n ? { findings: version.findings as GateFinding[] }\n : {}),\n ...(Array.isArray(version.stages)\n ? { stages: version.stages as GateStageResult[] }\n : {}),\n })),\n }));\n\n return { status: 'ok', integrations };\n}\n\n/** Guard for the \"still unauthorized after one refresh\" terminal case. */\nexport function stillUnauthorized(env: AuthEnvironment): never {\n throw authRequiredError(\n `The session for ${env.host} could not authorize the submission.`,\n loginHint(env),\n );\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction isRedirect(status: number): boolean {\n return status >= 300 && status < 400;\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nfunction num(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n"]}
|
package/dist/repo/publish.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface PackageRow {
|
|
|
27
27
|
findings?: Finding[];
|
|
28
28
|
/** Integration submissions: what the host answered. */
|
|
29
29
|
receipt?: Record<string, unknown>;
|
|
30
|
+
/** The full name the host gave a submitted integration (`acme/google-drive`). */
|
|
31
|
+
displayName?: string;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* `ekanos publish [name...] | --all [--dry-run]` in a repo.
|
|
@@ -35,8 +37,9 @@ export interface PackageRow {
|
|
|
35
37
|
* use them (a same-repo integration is not an override, so it is ordered
|
|
36
38
|
* first, never refused). Every package passes its own gate before anything is
|
|
37
39
|
* sent. `--dry-run` also builds each artifact under `.ekanos/publish/` and
|
|
38
|
-
* touches no network. A real run submits the
|
|
39
|
-
* existing path;
|
|
40
|
-
*
|
|
40
|
+
* touches no network. A real run submits the packages in order through the
|
|
41
|
+
* existing path; an experience goes to a host that advertises experience
|
|
42
|
+
* submissions, and against an older host that cannot admit one the run ends
|
|
43
|
+
* with that refusal after the rest were submitted.
|
|
41
44
|
*/
|
|
42
45
|
export declare function runRepoPublish(ctx: CliContext, args: RepoPublishArgs): Promise<ExitCode>;
|
package/dist/repo/publish.js
CHANGED
|
@@ -3,13 +3,19 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { buildManifest, escalateForPublish, prepareIntegrationSubmission, readProjectVersion, submitPrepared, } from '../commands/publish.js';
|
|
5
5
|
import { CliError, gateFailedError, preconditionError, usageError, validationError, } from '../errors.js';
|
|
6
|
-
import { EXPERIENCE_NOT_SUPPORTED, gateExperience, packExperience, } from '../experience/publish.js';
|
|
6
|
+
import { EXPERIENCE_NOT_SUPPORTED, gateExperience, packExperience, submitExperience, } from '../experience/publish.js';
|
|
7
7
|
import { packProject } from '../pack.js';
|
|
8
8
|
import { loadProject } from '../project.js';
|
|
9
9
|
import { shortHost } from '../ui.js';
|
|
10
10
|
import { collectAllFindings } from '../validate-findings.js';
|
|
11
11
|
export const PUBLISH_ARTIFACT_DIR = path.join('.ekanos', 'publish');
|
|
12
|
-
|
|
12
|
+
/** What an integration's declared availability means at submission. */
|
|
13
|
+
function availabilityNote(pkg) {
|
|
14
|
+
var _a;
|
|
15
|
+
return ((_a = pkg.availability) === null || _a === void 0 ? void 0 : _a.mode) === 'global'
|
|
16
|
+
? `needs an approved global name (run "ekanos names claim ${pkg.name}"); without one the publish is refused`
|
|
17
|
+
: 'publishes restricted, named under your Source namespace';
|
|
18
|
+
}
|
|
13
19
|
function loadRepo(root) {
|
|
14
20
|
try {
|
|
15
21
|
return discoverRepo(root);
|
|
@@ -40,12 +46,12 @@ function artifactName(pkg) {
|
|
|
40
46
|
return `${pkg.name}-${pkg.version}`;
|
|
41
47
|
}
|
|
42
48
|
function renderTable(ctx, rows) {
|
|
43
|
-
return ctx.ui.table(['package', 'kind', 'version', 'availability', 'artifact', 'status'], rows.map((row) => { var _a; return [
|
|
44
|
-
row.name,
|
|
49
|
+
return ctx.ui.table(['package', 'kind', 'version', 'availability', 'artifact', 'status'], rows.map((row) => { var _a, _b; return [
|
|
50
|
+
(_a = row.displayName) !== null && _a !== void 0 ? _a : row.name,
|
|
45
51
|
row.kind,
|
|
46
52
|
row.version,
|
|
47
53
|
row.availability,
|
|
48
|
-
(
|
|
54
|
+
(_b = row.artifact) !== null && _b !== void 0 ? _b : '-',
|
|
49
55
|
row.detail ? `${row.status}: ${row.detail}` : row.status,
|
|
50
56
|
]; }));
|
|
51
57
|
}
|
|
@@ -129,9 +135,10 @@ async function checkExperience(pkg, repoRoot, build, env) {
|
|
|
129
135
|
* use them (a same-repo integration is not an override, so it is ordered
|
|
130
136
|
* first, never refused). Every package passes its own gate before anything is
|
|
131
137
|
* sent. `--dry-run` also builds each artifact under `.ekanos/publish/` and
|
|
132
|
-
* touches no network. A real run submits the
|
|
133
|
-
* existing path;
|
|
134
|
-
*
|
|
138
|
+
* touches no network. A real run submits the packages in order through the
|
|
139
|
+
* existing path; an experience goes to a host that advertises experience
|
|
140
|
+
* submissions, and against an older host that cannot admit one the run ends
|
|
141
|
+
* with that refusal after the rest were submitted.
|
|
135
142
|
*/
|
|
136
143
|
export async function runRepoPublish(ctx, args) {
|
|
137
144
|
const repo = loadRepo(args.repoRoot);
|
|
@@ -150,7 +157,7 @@ export async function runRepoPublish(ctx, args) {
|
|
|
150
157
|
const warnings = plan.unselectedDependencies.map((dep) => `${dep.experience} uses ${dep.integration}, which is in this repo but not in this publish: publish ${dep.integration} first, or make sure it is already published at a compatible version.`);
|
|
151
158
|
const notes = plan.order.flatMap((pkg) => pkg.kind === 'integration' && pkg.availability
|
|
152
159
|
? [
|
|
153
|
-
`${pkg.name}: ${describeAvailability(pkg.availability)} - ${
|
|
160
|
+
`${pkg.name}: ${describeAvailability(pkg.availability)} - ${availabilityNote(pkg)}.`,
|
|
154
161
|
]
|
|
155
162
|
: []);
|
|
156
163
|
if (!ctx.jsonMode) {
|
|
@@ -183,14 +190,38 @@ export async function runRepoPublish(ctx, args) {
|
|
|
183
190
|
let host = null;
|
|
184
191
|
for (const [index, pkg] of plan.order.entries()) {
|
|
185
192
|
const row = rows[index];
|
|
186
|
-
if (pkg.kind === 'experience') {
|
|
187
|
-
row.status = 'not supported by the host';
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
193
|
if (stop) {
|
|
191
194
|
row.status = 'not attempted';
|
|
192
195
|
continue;
|
|
193
196
|
}
|
|
197
|
+
if (pkg.kind === 'experience') {
|
|
198
|
+
try {
|
|
199
|
+
const single = Object.assign(Object.assign({}, args.publish), { dir: pkg.dir });
|
|
200
|
+
const gate = await gateExperience({
|
|
201
|
+
dir: pkg.dir,
|
|
202
|
+
dryRun: false,
|
|
203
|
+
env: args.publish.env,
|
|
204
|
+
});
|
|
205
|
+
const submission = await submitExperience(ctx, Object.assign(Object.assign({}, single), { dryRun: false }), gate);
|
|
206
|
+
if (submission.status === 'unsupported') {
|
|
207
|
+
host = submission.host;
|
|
208
|
+
row.status = 'not supported by the host';
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
row.status = 'submitted';
|
|
212
|
+
row.receipt = submission.data;
|
|
213
|
+
if (!ctx.jsonMode)
|
|
214
|
+
ctx.log(submission.human);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
if (!(error instanceof CliError))
|
|
218
|
+
throw error;
|
|
219
|
+
row.status = 'failed';
|
|
220
|
+
row.detail = error.message;
|
|
221
|
+
stop = error;
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
194
225
|
try {
|
|
195
226
|
const single = Object.assign(Object.assign({}, args.publish), { dir: pkg.dir });
|
|
196
227
|
const prepared = await prepareIntegrationSubmission(ctx, single);
|
|
@@ -198,6 +229,9 @@ export async function runRepoPublish(ctx, args) {
|
|
|
198
229
|
const submitted = await submitPrepared(ctx, single, prepared);
|
|
199
230
|
row.status = 'submitted';
|
|
200
231
|
row.receipt = submitted.data;
|
|
232
|
+
if (typeof submitted.data.displayName === 'string') {
|
|
233
|
+
row.displayName = submitted.data.displayName;
|
|
234
|
+
}
|
|
201
235
|
if (!ctx.jsonMode)
|
|
202
236
|
ctx.log(submitted.human);
|
|
203
237
|
}
|
|
@@ -218,7 +252,7 @@ export async function runRepoPublish(ctx, args) {
|
|
|
218
252
|
if (rows.some((row) => row.status === 'not supported by the host')) {
|
|
219
253
|
if (!ctx.jsonMode)
|
|
220
254
|
ctx.log(summary(`Submitted${hostNote}.`));
|
|
221
|
-
return ctx.fail(gateFailedError(EXPERIENCE_NOT_SUPPORTED, 'The
|
|
255
|
+
return ctx.fail(gateFailedError(EXPERIENCE_NOT_SUPPORTED, 'The other packages in this publish were submitted; run "ekanos publish --dry-run" to build and pack the experiences. This host has no admission path for experiences until it ships one.'), data({ published: true }));
|
|
222
256
|
}
|
|
223
257
|
return ctx.succeed(data({ published: true }), summary(`Submitted ${rows.length} package${rows.length === 1 ? '' : 's'}${hostNote}.`));
|
|
224
258
|
}
|
package/dist/repo/publish.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/repo/publish.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport {\n type RepoDiscovery,\n type RepoPackage,\n RepoManifestError,\n describeAvailability,\n discoverRepo,\n packageContaining,\n planRepoOrder,\n} from '@ekanos/integration-schema/repo';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport {\n type PublishArgs,\n buildManifest,\n escalateForPublish,\n prepareIntegrationSubmission,\n readProjectVersion,\n submitPrepared,\n} from '../commands/publish';\nimport type { CliContext } from '../context';\nimport {\n CliError,\n gateFailedError,\n preconditionError,\n usageError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n EXPERIENCE_NOT_SUPPORTED,\n gateExperience,\n packExperience,\n} from '../experience/publish';\nimport { packProject } from '../pack';\nimport { loadProject } from '../project';\nimport { shortHost } from '../ui';\nimport { collectAllFindings } from '../validate-findings';\n\nexport const PUBLISH_ARTIFACT_DIR = path.join('.ekanos', 'publish');\n\nexport interface RepoPublishArgs {\n repoRoot: string;\n /** Where the command was run: inside a package it narrows the selection. */\n cwd: string;\n /** Package names given on the command line. */\n names: readonly string[];\n all: boolean;\n /** The single-project publish arguments, reused for every integration. */\n publish: PublishArgs;\n}\n\nexport type PackageStatus =\n | 'ready'\n | 'built'\n | 'gate failed'\n | 'refused'\n | 'submitted'\n | 'failed'\n | 'not attempted'\n | 'not supported by the host';\n\nexport interface PackageRow {\n name: string;\n kind: 'integration' | 'experience';\n version: string;\n availability: string;\n /** Artifact directory relative to the repo root, when one was built. */\n artifact: string | null;\n status: PackageStatus;\n /** Why the status is not a success, one line. */\n detail?: string;\n findings?: Finding[];\n /** Integration submissions: what the host answered. */\n receipt?: Record<string, unknown>;\n}\n\nconst AVAILABILITY_NOTE =\n 'availability is set by a super admin today; the host does not take it at submission';\n\nfunction loadRepo(root: string): RepoDiscovery {\n try {\n return discoverRepo(root);\n } catch (error) {\n if (error instanceof RepoManifestError) {\n throw validationError(\n error.message,\n 'Fix the root ekanos.json: { \"kind\": \"repo\", \"packages\": [\"integrations/*\", \"experiences/*\"] }.',\n );\n }\n throw error;\n }\n}\n\nfunction select(repo: RepoDiscovery, args: RepoPublishArgs): string[] {\n if (args.all && args.names.length > 0) {\n throw usageError(\n 'Pass package names or --all, not both.',\n 'e.g. \"ekanos publish acme-crm\" or \"ekanos publish --all\".',\n );\n }\n if (args.names.length > 0) return [...args.names];\n if (args.all) return [];\n const here = packageContaining(repo, args.cwd);\n if (here) return [here.name];\n throw usageError(\n 'This is a repo: say what to publish.',\n `Name packages (\"ekanos publish ${repo.packages[0]?.name ?? '<package>'}\"), pass --all, or run it from inside a package. Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none yet; \"ekanos new integration <slug>\")'}.`,\n );\n}\n\nfunction artifactName(pkg: RepoPackage): string {\n return `${pkg.name}-${pkg.version}`;\n}\n\nfunction renderTable(ctx: CliContext, rows: readonly PackageRow[]): string {\n return ctx.ui.table(\n ['package', 'kind', 'version', 'availability', 'artifact', 'status'],\n rows.map((row) => [\n row.name,\n row.kind,\n row.version,\n row.availability,\n row.artifact ?? '-',\n row.detail ? `${row.status}: ${row.detail}` : row.status,\n ]),\n );\n}\n\n/** Gate one integration and, when asked, build its artifact. No network. */\nasync function checkIntegration(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'integration',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n readProjectVersion(pkg.dir);\n const loaded = loadProject(pkg.dir);\n const findings = escalateForPublish(await collectAllFindings(loaded));\n const errors = findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos validate\" in ${pkg.relDir}`,\n findings,\n };\n }\n if (!build) return row;\n\n const packed = packProject(loaded.projectDir);\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(\n path.join(dir, `${artifactName(pkg)}.tgz`),\n packed.archive,\n );\n fs.writeFileSync(\n path.join(dir, 'manifest.json'),\n `${buildManifest(loaded, packed.files)}\\n`,\n );\n const contract = path.join(pkg.dir, 'contract.json');\n if (fs.existsSync(contract)) {\n fs.copyFileSync(contract, path.join(dir, 'contract.json'));\n }\n\n return {\n ...row,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n ...(findings.length > 0 ? { findings } : {}),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/** Gate one experience and, when asked, build and pack it. No network. */\nasync function checkExperience(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n env: Record<string, string | undefined>,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'experience',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n const gate = await gateExperience({ dir: pkg.dir, dryRun: true, env });\n const errors = gate.result.findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos check\" in ${pkg.relDir}`,\n findings: gate.result.findings,\n };\n }\n if (!build) return row;\n\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n const packed = await packExperience(gate, dir);\n const manifest = path.join(pkg.dir, 'manifest.json');\n if (fs.existsSync(manifest)) {\n fs.copyFileSync(manifest, path.join(dir, 'manifest.json'));\n }\n\n return {\n ...row,\n version: packed.version,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/**\n * `ekanos publish [name...] | --all [--dry-run]` in a repo.\n *\n * The plan is the selected packages, integrations before the experiences that\n * use them (a same-repo integration is not an override, so it is ordered\n * first, never refused). Every package passes its own gate before anything is\n * sent. `--dry-run` also builds each artifact under `.ekanos/publish/` and\n * touches no network. A real run submits the integrations in order through the\n * existing path; the host cannot admit an experience yet, so selected\n * experiences end the run with that refusal.\n */\nexport async function runRepoPublish(\n ctx: CliContext,\n args: RepoPublishArgs,\n): Promise<ExitCode> {\n const repo = loadRepo(args.repoRoot);\n const names = select(repo, args);\n const plan = planRepoOrder(repo, names);\n\n if (plan.unknown.length > 0) {\n throw usageError(\n `No package named ${plan.unknown.map((n) => `\"${n}\"`).join(', ')} in this repo.`,\n `Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none)'}.`,\n );\n }\n if (args.all && repo.problems.length > 0) {\n throw validationError(\n `${repo.problems.length} package${repo.problems.length === 1 ? ' is' : 's are'} unreadable: ${repo.problems.map((p) => `${p.where} (${p.message})`).join('; ')}`,\n 'Fix or remove them, then re-run \"ekanos publish --all\".',\n );\n }\n if (plan.order.length === 0) {\n throw preconditionError(\n 'This repo has no packages yet.',\n 'Add one with \"ekanos new integration <slug>\" or \"ekanos new experience <slug>\".',\n );\n }\n\n const dryRun = args.publish.dryRun === true;\n const warnings = plan.unselectedDependencies.map(\n (dep) =>\n `${dep.experience} uses ${dep.integration}, which is in this repo but not in this publish: publish ${dep.integration} first, or make sure it is already published at a compatible version.`,\n );\n const notes = plan.order.flatMap((pkg) =>\n pkg.kind === 'integration' && pkg.availability\n ? [\n `${pkg.name}: ${describeAvailability(pkg.availability)} - ${AVAILABILITY_NOTE}.`,\n ]\n : [],\n );\n\n if (!ctx.jsonMode) {\n ctx.log(\n `${dryRun ? 'Dry run: ' : ''}publishing ${plan.order.length} package${plan.order.length === 1 ? '' : 's'} in this order:`,\n );\n plan.order.forEach((pkg, index) =>\n ctx.log(\n ` ${index + 1}. ${pkg.name} (${pkg.kind} ${pkg.version}, ${describeAvailability(pkg.availability)})`,\n ),\n );\n for (const warning of warnings) ctx.warn(` warning: ${warning}`);\n }\n\n // Gate (and, on a dry run, build) every package before anything is sent.\n const rows: PackageRow[] = [];\n for (const pkg of plan.order) {\n rows.push(\n pkg.kind === 'integration'\n ? await checkIntegration(pkg, repo.root, dryRun)\n : await checkExperience(pkg, repo.root, dryRun, args.publish.env),\n );\n }\n\n const data = (extra: Record<string, unknown> = {}) => ({\n repo: repo.root,\n dryRun,\n order: plan.order.map((p) => p.name),\n warnings,\n notes,\n packages: rows,\n ...extra,\n });\n const summary = (title: string) =>\n [title, '', renderTable(ctx, rows), ...notes.map((n) => `note: ${n}`)].join(\n '\\n',\n );\n\n const failed = rows.filter(\n (row) => row.status === 'gate failed' || row.status === 'refused',\n );\n if (failed.length > 0) {\n if (!ctx.jsonMode) ctx.log(summary('Nothing was published.'));\n return ctx.fail(\n gateFailedError(\n `${failed.length} of ${rows.length} package${rows.length === 1 ? '' : 's'} did not pass: ${failed.map((r) => r.name).join(', ')}.`,\n 'Fix the packages named above (the status column says what), then re-run \"ekanos publish\". Nothing was submitted.',\n ),\n data(),\n );\n }\n\n if (dryRun) {\n return ctx.succeed(\n data({ published: false }),\n summary(\n `Dry run: ${rows.length} package${rows.length === 1 ? '' : 's'} built under ${PUBLISH_ARTIFACT_DIR}. Nothing was published.`,\n ),\n );\n }\n\n // Real publish: integrations in plan order through the existing path.\n let stop: CliError | null = null;\n let host: string | null = null;\n for (const [index, pkg] of plan.order.entries()) {\n const row = rows[index] as PackageRow;\n if (pkg.kind === 'experience') {\n row.status = 'not supported by the host';\n continue;\n }\n if (stop) {\n row.status = 'not attempted';\n continue;\n }\n try {\n const single: PublishArgs = { ...args.publish, dir: pkg.dir };\n const prepared = await prepareIntegrationSubmission(ctx, single);\n host = prepared.env.host;\n const submitted = await submitPrepared(ctx, single, prepared);\n row.status = 'submitted';\n row.receipt = submitted.data;\n if (!ctx.jsonMode) ctx.log(submitted.human);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n row.status = 'failed';\n row.detail = error.message;\n stop = error;\n }\n }\n\n const hostNote = host ? ` to ${shortHost(host)}` : '';\n if (stop) {\n if (!ctx.jsonMode) ctx.log(summary('Publish stopped.'));\n return ctx.fail(stop, data());\n }\n if (rows.some((row) => row.status === 'not supported by the host')) {\n if (!ctx.jsonMode) ctx.log(summary(`Submitted${hostNote}.`));\n return ctx.fail(\n gateFailedError(\n EXPERIENCE_NOT_SUPPORTED,\n 'The integrations in this publish were submitted; run \"ekanos publish --dry-run\" to build and pack the experiences. The host has no admission path for experiences until it ships one.',\n ),\n data({ published: true }),\n );\n }\n\n return ctx.succeed(\n data({ published: true }),\n summary(\n `Submitted ${rows.length} package${rows.length === 1 ? '' : 's'}${hostNote}.`,\n ),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/repo/publish.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport {\n type RepoDiscovery,\n type RepoPackage,\n RepoManifestError,\n describeAvailability,\n discoverRepo,\n packageContaining,\n planRepoOrder,\n} from '@ekanos/integration-schema/repo';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport {\n type PublishArgs,\n buildManifest,\n escalateForPublish,\n prepareIntegrationSubmission,\n readProjectVersion,\n submitPrepared,\n} from '../commands/publish';\nimport type { CliContext } from '../context';\nimport {\n CliError,\n gateFailedError,\n preconditionError,\n usageError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n EXPERIENCE_NOT_SUPPORTED,\n gateExperience,\n packExperience,\n submitExperience,\n} from '../experience/publish';\nimport { packProject } from '../pack';\nimport { loadProject } from '../project';\nimport { shortHost } from '../ui';\nimport { collectAllFindings } from '../validate-findings';\n\nexport const PUBLISH_ARTIFACT_DIR = path.join('.ekanos', 'publish');\n\nexport interface RepoPublishArgs {\n repoRoot: string;\n /** Where the command was run: inside a package it narrows the selection. */\n cwd: string;\n /** Package names given on the command line. */\n names: readonly string[];\n all: boolean;\n /** The single-project publish arguments, reused for every integration. */\n publish: PublishArgs;\n}\n\nexport type PackageStatus =\n | 'ready'\n | 'built'\n | 'gate failed'\n | 'refused'\n | 'submitted'\n | 'failed'\n | 'not attempted'\n | 'not supported by the host';\n\nexport interface PackageRow {\n name: string;\n kind: 'integration' | 'experience';\n version: string;\n availability: string;\n /** Artifact directory relative to the repo root, when one was built. */\n artifact: string | null;\n status: PackageStatus;\n /** Why the status is not a success, one line. */\n detail?: string;\n findings?: Finding[];\n /** Integration submissions: what the host answered. */\n receipt?: Record<string, unknown>;\n /** The full name the host gave a submitted integration (`acme/google-drive`). */\n displayName?: string;\n}\n\n/** What an integration's declared availability means at submission. */\nfunction availabilityNote(pkg: RepoPackage): string {\n return pkg.availability?.mode === 'global'\n ? `needs an approved global name (run \"ekanos names claim ${pkg.name}\"); without one the publish is refused`\n : 'publishes restricted, named under your Source namespace';\n}\n\nfunction loadRepo(root: string): RepoDiscovery {\n try {\n return discoverRepo(root);\n } catch (error) {\n if (error instanceof RepoManifestError) {\n throw validationError(\n error.message,\n 'Fix the root ekanos.json: { \"kind\": \"repo\", \"packages\": [\"integrations/*\", \"experiences/*\"] }.',\n );\n }\n throw error;\n }\n}\n\nfunction select(repo: RepoDiscovery, args: RepoPublishArgs): string[] {\n if (args.all && args.names.length > 0) {\n throw usageError(\n 'Pass package names or --all, not both.',\n 'e.g. \"ekanos publish acme-crm\" or \"ekanos publish --all\".',\n );\n }\n if (args.names.length > 0) return [...args.names];\n if (args.all) return [];\n const here = packageContaining(repo, args.cwd);\n if (here) return [here.name];\n throw usageError(\n 'This is a repo: say what to publish.',\n `Name packages (\"ekanos publish ${repo.packages[0]?.name ?? '<package>'}\"), pass --all, or run it from inside a package. Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none yet; \"ekanos new integration <slug>\")'}.`,\n );\n}\n\nfunction artifactName(pkg: RepoPackage): string {\n return `${pkg.name}-${pkg.version}`;\n}\n\nfunction renderTable(ctx: CliContext, rows: readonly PackageRow[]): string {\n return ctx.ui.table(\n ['package', 'kind', 'version', 'availability', 'artifact', 'status'],\n rows.map((row) => [\n row.displayName ?? row.name,\n row.kind,\n row.version,\n row.availability,\n row.artifact ?? '-',\n row.detail ? `${row.status}: ${row.detail}` : row.status,\n ]),\n );\n}\n\n/** Gate one integration and, when asked, build its artifact. No network. */\nasync function checkIntegration(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'integration',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n readProjectVersion(pkg.dir);\n const loaded = loadProject(pkg.dir);\n const findings = escalateForPublish(await collectAllFindings(loaded));\n const errors = findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos validate\" in ${pkg.relDir}`,\n findings,\n };\n }\n if (!build) return row;\n\n const packed = packProject(loaded.projectDir);\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(\n path.join(dir, `${artifactName(pkg)}.tgz`),\n packed.archive,\n );\n fs.writeFileSync(\n path.join(dir, 'manifest.json'),\n `${buildManifest(loaded, packed.files)}\\n`,\n );\n const contract = path.join(pkg.dir, 'contract.json');\n if (fs.existsSync(contract)) {\n fs.copyFileSync(contract, path.join(dir, 'contract.json'));\n }\n\n return {\n ...row,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n ...(findings.length > 0 ? { findings } : {}),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/** Gate one experience and, when asked, build and pack it. No network. */\nasync function checkExperience(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n env: Record<string, string | undefined>,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'experience',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n const gate = await gateExperience({ dir: pkg.dir, dryRun: true, env });\n const errors = gate.result.findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos check\" in ${pkg.relDir}`,\n findings: gate.result.findings,\n };\n }\n if (!build) return row;\n\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n const packed = await packExperience(gate, dir);\n const manifest = path.join(pkg.dir, 'manifest.json');\n if (fs.existsSync(manifest)) {\n fs.copyFileSync(manifest, path.join(dir, 'manifest.json'));\n }\n\n return {\n ...row,\n version: packed.version,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/**\n * `ekanos publish [name...] | --all [--dry-run]` in a repo.\n *\n * The plan is the selected packages, integrations before the experiences that\n * use them (a same-repo integration is not an override, so it is ordered\n * first, never refused). Every package passes its own gate before anything is\n * sent. `--dry-run` also builds each artifact under `.ekanos/publish/` and\n * touches no network. A real run submits the packages in order through the\n * existing path; an experience goes to a host that advertises experience\n * submissions, and against an older host that cannot admit one the run ends\n * with that refusal after the rest were submitted.\n */\nexport async function runRepoPublish(\n ctx: CliContext,\n args: RepoPublishArgs,\n): Promise<ExitCode> {\n const repo = loadRepo(args.repoRoot);\n const names = select(repo, args);\n const plan = planRepoOrder(repo, names);\n\n if (plan.unknown.length > 0) {\n throw usageError(\n `No package named ${plan.unknown.map((n) => `\"${n}\"`).join(', ')} in this repo.`,\n `Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none)'}.`,\n );\n }\n if (args.all && repo.problems.length > 0) {\n throw validationError(\n `${repo.problems.length} package${repo.problems.length === 1 ? ' is' : 's are'} unreadable: ${repo.problems.map((p) => `${p.where} (${p.message})`).join('; ')}`,\n 'Fix or remove them, then re-run \"ekanos publish --all\".',\n );\n }\n if (plan.order.length === 0) {\n throw preconditionError(\n 'This repo has no packages yet.',\n 'Add one with \"ekanos new integration <slug>\" or \"ekanos new experience <slug>\".',\n );\n }\n\n const dryRun = args.publish.dryRun === true;\n const warnings = plan.unselectedDependencies.map(\n (dep) =>\n `${dep.experience} uses ${dep.integration}, which is in this repo but not in this publish: publish ${dep.integration} first, or make sure it is already published at a compatible version.`,\n );\n const notes = plan.order.flatMap((pkg) =>\n pkg.kind === 'integration' && pkg.availability\n ? [\n `${pkg.name}: ${describeAvailability(pkg.availability)} - ${availabilityNote(pkg)}.`,\n ]\n : [],\n );\n\n if (!ctx.jsonMode) {\n ctx.log(\n `${dryRun ? 'Dry run: ' : ''}publishing ${plan.order.length} package${plan.order.length === 1 ? '' : 's'} in this order:`,\n );\n plan.order.forEach((pkg, index) =>\n ctx.log(\n ` ${index + 1}. ${pkg.name} (${pkg.kind} ${pkg.version}, ${describeAvailability(pkg.availability)})`,\n ),\n );\n for (const warning of warnings) ctx.warn(` warning: ${warning}`);\n }\n\n // Gate (and, on a dry run, build) every package before anything is sent.\n const rows: PackageRow[] = [];\n for (const pkg of plan.order) {\n rows.push(\n pkg.kind === 'integration'\n ? await checkIntegration(pkg, repo.root, dryRun)\n : await checkExperience(pkg, repo.root, dryRun, args.publish.env),\n );\n }\n\n const data = (extra: Record<string, unknown> = {}) => ({\n repo: repo.root,\n dryRun,\n order: plan.order.map((p) => p.name),\n warnings,\n notes,\n packages: rows,\n ...extra,\n });\n const summary = (title: string) =>\n [title, '', renderTable(ctx, rows), ...notes.map((n) => `note: ${n}`)].join(\n '\\n',\n );\n\n const failed = rows.filter(\n (row) => row.status === 'gate failed' || row.status === 'refused',\n );\n if (failed.length > 0) {\n if (!ctx.jsonMode) ctx.log(summary('Nothing was published.'));\n return ctx.fail(\n gateFailedError(\n `${failed.length} of ${rows.length} package${rows.length === 1 ? '' : 's'} did not pass: ${failed.map((r) => r.name).join(', ')}.`,\n 'Fix the packages named above (the status column says what), then re-run \"ekanos publish\". Nothing was submitted.',\n ),\n data(),\n );\n }\n\n if (dryRun) {\n return ctx.succeed(\n data({ published: false }),\n summary(\n `Dry run: ${rows.length} package${rows.length === 1 ? '' : 's'} built under ${PUBLISH_ARTIFACT_DIR}. Nothing was published.`,\n ),\n );\n }\n\n // Real publish: integrations in plan order through the existing path.\n let stop: CliError | null = null;\n let host: string | null = null;\n for (const [index, pkg] of plan.order.entries()) {\n const row = rows[index] as PackageRow;\n if (stop) {\n row.status = 'not attempted';\n continue;\n }\n if (pkg.kind === 'experience') {\n try {\n const single: PublishArgs = { ...args.publish, dir: pkg.dir };\n const gate = await gateExperience({\n dir: pkg.dir,\n dryRun: false,\n env: args.publish.env,\n });\n const submission = await submitExperience(\n ctx,\n { ...single, dryRun: false },\n gate,\n );\n if (submission.status === 'unsupported') {\n host = submission.host;\n row.status = 'not supported by the host';\n continue;\n }\n row.status = 'submitted';\n row.receipt = submission.data;\n if (!ctx.jsonMode) ctx.log(submission.human);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n row.status = 'failed';\n row.detail = error.message;\n stop = error;\n }\n continue;\n }\n try {\n const single: PublishArgs = { ...args.publish, dir: pkg.dir };\n const prepared = await prepareIntegrationSubmission(ctx, single);\n host = prepared.env.host;\n const submitted = await submitPrepared(ctx, single, prepared);\n row.status = 'submitted';\n row.receipt = submitted.data;\n if (typeof submitted.data.displayName === 'string') {\n row.displayName = submitted.data.displayName;\n }\n if (!ctx.jsonMode) ctx.log(submitted.human);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n row.status = 'failed';\n row.detail = error.message;\n stop = error;\n }\n }\n\n const hostNote = host ? ` to ${shortHost(host)}` : '';\n if (stop) {\n if (!ctx.jsonMode) ctx.log(summary('Publish stopped.'));\n return ctx.fail(stop, data());\n }\n if (rows.some((row) => row.status === 'not supported by the host')) {\n if (!ctx.jsonMode) ctx.log(summary(`Submitted${hostNote}.`));\n return ctx.fail(\n gateFailedError(\n EXPERIENCE_NOT_SUPPORTED,\n 'The other packages in this publish were submitted; run \"ekanos publish --dry-run\" to build and pack the experiences. This host has no admission path for experiences until it ships one.',\n ),\n data({ published: true }),\n );\n }\n\n return ctx.succeed(\n data({ published: true }),\n summary(\n `Submitted ${rows.length} package${rows.length === 1 ? '' : 's'}${hostNote}.`,\n ),\n );\n}\n"]}
|
|
@@ -4,6 +4,7 @@ export declare const SourceProductSchema: z.ZodObject<{
|
|
|
4
4
|
name: z.ZodString;
|
|
5
5
|
kind: z.ZodEnum<{
|
|
6
6
|
experience: "experience";
|
|
7
|
+
feature: "feature";
|
|
7
8
|
integration: "integration";
|
|
8
9
|
}>;
|
|
9
10
|
enabled: z.ZodBoolean;
|
|
@@ -31,7 +32,7 @@ export declare function listSourceProducts(host: string, token: string, source:
|
|
|
31
32
|
products: {
|
|
32
33
|
slug: string;
|
|
33
34
|
name: string;
|
|
34
|
-
kind: "experience" | "integration";
|
|
35
|
+
kind: "experience" | "feature" | "integration";
|
|
35
36
|
enabled: boolean;
|
|
36
37
|
lever: "entitlement" | "flag" | "plan";
|
|
37
38
|
registered: boolean;
|
|
@@ -53,7 +54,7 @@ export declare function setSourceProduct(host: string, token: string, input: {
|
|
|
53
54
|
product: {
|
|
54
55
|
slug: string;
|
|
55
56
|
name: string;
|
|
56
|
-
kind: "experience" | "integration";
|
|
57
|
+
kind: "experience" | "feature" | "integration";
|
|
57
58
|
enabled: boolean;
|
|
58
59
|
lever: "entitlement" | "flag" | "plan";
|
|
59
60
|
registered: boolean;
|
|
@@ -29,7 +29,7 @@ const SourceRefSchema = z.object({
|
|
|
29
29
|
export const SourceProductSchema = z.object({
|
|
30
30
|
slug: z.string(),
|
|
31
31
|
name: z.string(),
|
|
32
|
-
kind: z.enum(['experience', 'integration']),
|
|
32
|
+
kind: z.enum(['feature', 'experience', 'integration']),
|
|
33
33
|
enabled: z.boolean(),
|
|
34
34
|
lever: z.enum(['flag', 'plan', 'entitlement']),
|
|
35
35
|
registered: z.boolean(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-products-api.js","sourceRoot":"","sources":["../src/source-products-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n usageError,\n} from './errors';\n\n/**\n * The CLI's client for a Source's products (`ekanos sources products`).\n *\n * GET /api/partner/sources/products?source=<slug|id>\n * → { source, products: [{ slug, name, kind, enabled, lever, registered, owned }],\n * canEnable, canEnableOwned }\n * PUT /api/partner/sources/products/<product>?source=<slug|id> enable\n * DELETE /api/partner/sources/products/<product>?source=<slug|id> disable\n * → { source, product, changed }\n *\n * → 401 / 3xx refresh once and retry\n * → 403 SOURCE_ADMIN_REQUIRED no Source admin seat on the Source\n * → 403 SUPER_ADMIN_REQUIRED not an integration the Source built: a\n * super-admin grant, which an `ekanos login`\n * session never carries\n * → 409 REFUSED a dependency or demo-only guard refused\n *\n * The shapes are read here rather than from `@ekanos/integration-schema`, so\n * this CLI works against a host without a schema release.\n */\n\nconst SourceRefSchema = z.object({\n id: z.string(),\n slug: z.string(),\n name: z.string().nullable(),\n});\n\nexport const SourceProductSchema = z.object({\n slug: z.string(),\n name: z.string(),\n kind: z.enum(['experience', 'integration']),\n enabled: z.boolean(),\n lever: z.enum(['flag', 'plan', 'entitlement']),\n registered: z.boolean(),\n /** The Source built it, so its Source admin may switch it. Absent on an\n * older host, which allowed no Source admin to. */\n owned: z.boolean().optional().default(false),\n});\nexport type SourceProduct = z.infer<typeof SourceProductSchema>;\n\nconst ListSchema = z.object({\n source: SourceRefSchema,\n products: z.array(SourceProductSchema),\n canEnable: z.boolean(),\n canEnableOwned: z.boolean().optional().default(false),\n});\nconst ChangeSchema = z.object({\n source: SourceRefSchema,\n product: SourceProductSchema,\n changed: z.boolean(),\n});\n\nexport type SourceProductsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nexport function listSourceProducts(\n host: string,\n token: string,\n source: string,\n) {\n return call(host, token, ListSchema, {\n method: 'GET',\n path: `/api/partner/sources/products?${new URLSearchParams({ source })}`,\n });\n}\n\nexport function setSourceProduct(\n host: string,\n token: string,\n input: { source: string; product: string; enabled: boolean },\n) {\n return call(host, token, ChangeSchema, {\n method: input.enabled ? 'PUT' : 'DELETE',\n path: `/api/partner/sources/products/${encodeURIComponent(input.product)}?${new URLSearchParams({ source: input.source })}`,\n });\n}\n\nasync function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: { method: 'GET' | 'PUT' | 'DELETE'; path: string },\n): Promise<SourceProductsApiResult<T>> {\n const response = await request(`${host}${init.path}`, {\n method: init.method,\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\n\n if (response.status === 403 && code === 'MFA_REQUIRED') {\n throw authRequiredError(\n message ?? `${host} requires multi-factor authentication.`,\n 'Complete MFA in the browser, then run \"ekanos login\" again.',\n );\n }\n if (response.status === 403 && code === 'SUPER_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Only a super-admin enables this product for a Source.',\n 'A Source admin switches only the integrations their Source built. Anything else a super-admin does in the browser: /admin/sources → the Source → Products (Experiences are switched on the same page).',\n );\n }\n if (response.status === 403 && code === 'SOURCE_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Switching a product needs a Source admin seat on the Source.',\n 'Run \"ekanos sources\" to see your seats; a Source admin grants the seat.',\n );\n }\n if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'PRODUCT_NOT_FOUND'\n ? '\"ekanos sources products\" lists the catalog.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409) {\n throw invalidStateError(\n message ?? 'The host refused the change.',\n '\"ekanos sources products\" shows what this Source has.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos sources --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path.split('?')[0]} responded ${response.status}.`,\n response.status >= 500\n ? 'The Ekanos deployment returned a server error. Retry shortly.'\n : 'Confirm the host runs a build that includes the source products routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/sources/products answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the source products contract.',\n );\n }\n return { status: 'ok', data: parsed.data };\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"source-products-api.js","sourceRoot":"","sources":["../src/source-products-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n usageError,\n} from './errors';\n\n/**\n * The CLI's client for a Source's products (`ekanos sources products`).\n *\n * GET /api/partner/sources/products?source=<slug|id>\n * → { source, products: [{ slug, name, kind, enabled, lever, registered, owned }],\n * canEnable, canEnableOwned }\n * PUT /api/partner/sources/products/<product>?source=<slug|id> enable\n * DELETE /api/partner/sources/products/<product>?source=<slug|id> disable\n * → { source, product, changed }\n *\n * → 401 / 3xx refresh once and retry\n * → 403 SOURCE_ADMIN_REQUIRED no Source admin seat on the Source\n * → 403 SUPER_ADMIN_REQUIRED not an integration the Source built: a\n * super-admin grant, which an `ekanos login`\n * session never carries\n * → 409 REFUSED a dependency or demo-only guard refused\n *\n * The shapes are read here rather than from `@ekanos/integration-schema`, so\n * this CLI works against a host without a schema release.\n */\n\nconst SourceRefSchema = z.object({\n id: z.string(),\n slug: z.string(),\n name: z.string().nullable(),\n});\n\nexport const SourceProductSchema = z.object({\n slug: z.string(),\n name: z.string(),\n kind: z.enum(['feature', 'experience', 'integration']),\n enabled: z.boolean(),\n lever: z.enum(['flag', 'plan', 'entitlement']),\n registered: z.boolean(),\n /** The Source built it, so its Source admin may switch it. Absent on an\n * older host, which allowed no Source admin to. */\n owned: z.boolean().optional().default(false),\n});\nexport type SourceProduct = z.infer<typeof SourceProductSchema>;\n\nconst ListSchema = z.object({\n source: SourceRefSchema,\n products: z.array(SourceProductSchema),\n canEnable: z.boolean(),\n canEnableOwned: z.boolean().optional().default(false),\n});\nconst ChangeSchema = z.object({\n source: SourceRefSchema,\n product: SourceProductSchema,\n changed: z.boolean(),\n});\n\nexport type SourceProductsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nexport function listSourceProducts(\n host: string,\n token: string,\n source: string,\n) {\n return call(host, token, ListSchema, {\n method: 'GET',\n path: `/api/partner/sources/products?${new URLSearchParams({ source })}`,\n });\n}\n\nexport function setSourceProduct(\n host: string,\n token: string,\n input: { source: string; product: string; enabled: boolean },\n) {\n return call(host, token, ChangeSchema, {\n method: input.enabled ? 'PUT' : 'DELETE',\n path: `/api/partner/sources/products/${encodeURIComponent(input.product)}?${new URLSearchParams({ source: input.source })}`,\n });\n}\n\nasync function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: { method: 'GET' | 'PUT' | 'DELETE'; path: string },\n): Promise<SourceProductsApiResult<T>> {\n const response = await request(`${host}${init.path}`, {\n method: init.method,\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\n\n if (response.status === 403 && code === 'MFA_REQUIRED') {\n throw authRequiredError(\n message ?? `${host} requires multi-factor authentication.`,\n 'Complete MFA in the browser, then run \"ekanos login\" again.',\n );\n }\n if (response.status === 403 && code === 'SUPER_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Only a super-admin enables this product for a Source.',\n 'A Source admin switches only the integrations their Source built. Anything else a super-admin does in the browser: /admin/sources → the Source → Products (Experiences are switched on the same page).',\n );\n }\n if (response.status === 403 && code === 'SOURCE_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Switching a product needs a Source admin seat on the Source.',\n 'Run \"ekanos sources\" to see your seats; a Source admin grants the seat.',\n );\n }\n if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'PRODUCT_NOT_FOUND'\n ? '\"ekanos sources products\" lists the catalog.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409) {\n throw invalidStateError(\n message ?? 'The host refused the change.',\n '\"ekanos sources products\" shows what this Source has.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos sources --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path.split('?')[0]} responded ${response.status}.`,\n response.status >= 500\n ? 'The Ekanos deployment returned a server error. Retry shortly.'\n : 'Confirm the host runs a build that includes the source products routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/sources/products answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the source products contract.',\n );\n }\n return { status: 'ok', data: parsed.data };\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n"]}
|
|
@@ -31,6 +31,7 @@ export declare const TeamConnectPlanSchema: z.ZodObject<{
|
|
|
31
31
|
name: z.ZodString;
|
|
32
32
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
33
33
|
experience: "experience";
|
|
34
|
+
feature: "feature";
|
|
34
35
|
integration: "integration";
|
|
35
36
|
}>>;
|
|
36
37
|
}, z.core.$strip>;
|
|
@@ -116,6 +117,7 @@ declare const ShowSchema: z.ZodObject<{
|
|
|
116
117
|
name: z.ZodString;
|
|
117
118
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
118
119
|
experience: "experience";
|
|
120
|
+
feature: "feature";
|
|
119
121
|
integration: "integration";
|
|
120
122
|
}>>;
|
|
121
123
|
}, z.core.$strip>;
|
|
@@ -196,6 +198,7 @@ declare const StartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
196
198
|
name: z.ZodString;
|
|
197
199
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
198
200
|
experience: "experience";
|
|
201
|
+
feature: "feature";
|
|
199
202
|
integration: "integration";
|
|
200
203
|
}>>;
|
|
201
204
|
}, z.core.$strip>;
|
|
@@ -276,6 +279,7 @@ declare const StartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
276
279
|
name: z.ZodString;
|
|
277
280
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
278
281
|
experience: "experience";
|
|
282
|
+
feature: "feature";
|
|
279
283
|
integration: "integration";
|
|
280
284
|
}>>;
|
|
281
285
|
}, z.core.$strip>;
|
package/dist/team-connect-api.js
CHANGED
|
@@ -26,7 +26,7 @@ export const TeamConnectPlanSchema = z.object({
|
|
|
26
26
|
product: z.object({
|
|
27
27
|
slug: z.string(),
|
|
28
28
|
name: z.string(),
|
|
29
|
-
kind: z.enum(['experience', 'integration']).nullable(),
|
|
29
|
+
kind: z.enum(['feature', 'experience', 'integration']).nullable(),
|
|
30
30
|
}),
|
|
31
31
|
scope: z.enum(['team', 'user']),
|
|
32
32
|
/** The OAuth connection the plan is for; absent from an older host (`default`). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-connect-api.js","sourceRoot":"","sources":["../src/team-connect-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAuB,IAAI,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;QACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC5B,CAAC;IACF,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACtD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"team-connect-api.js","sourceRoot":"","sources":["../src/team-connect-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { type TeamsApiResult, call } from './teams-api';\n\n/**\n * The CLI's client for connecting a product for a team (`ekanos teams\n * connect`), for the team's Source admin.\n *\n * GET /api/partner/teams/<team>/integrations/<product>/connect?source=<slug>[&as=<email>][&connection=<id>]\n * → { plan }\n * POST /api/partner/teams/<team>/integrations/<product>/connect?source=<slug>\n * { as?, fields?, set?, connection? }\n * → { plan, result: \"activated\" } fields / no-fields\n * → { plan, result: \"consent\", authorizeUrl, expiresAt } oauth (fields too, for signIn \"app\")\n *\n * The plan's `mode`, decided by the host, says how the product is\n * connected. The shapes are read here rather than from\n * `@ekanos/integration-schema`, so this CLI works against a host without a\n * schema release.\n */\n\nexport const TeamConnectPlanSchema = z.object({\n source: z.object({\n id: z.string(),\n slug: z.string(),\n name: z.string().nullable(),\n }),\n team: z.object({ slug: z.string(), name: z.string() }),\n product: z.object({\n slug: z.string(),\n name: z.string(),\n kind: z.enum(['feature', 'experience', 'integration']).nullable(),\n }),\n scope: z.enum(['team', 'user']),\n /** The OAuth connection the plan is for; absent from an older host (`default`). */\n connectionId: z.string().optional(),\n mode: z.enum([\n 'unsupported',\n 'not-entitled',\n 'not-self-serve',\n 'works-as-shipped',\n 'oauth',\n 'fields',\n 'no-fields',\n 'browser',\n ]),\n /**\n * `oauth` mode: whose sign-in the link opens — the hosted partner\n * transport, or the app's own connect route (GitHub, ROAM), which then\n * activates with `fields`. Absent from an older host (`partner`).\n */\n signIn: z.enum(['partner', 'app']).optional(),\n reason: z.string().nullable(),\n fields: z.array(\n z.object({\n name: z.string(),\n label: z.string(),\n description: z.string().nullable(),\n sensitive: z.boolean(),\n required: z.boolean(),\n /**\n * How it is entered (`connect-fields.ts`). An older host sends only\n * plain fields, so absent reads as text.\n */\n type: z\n .enum(['text', 'number', 'boolean', 'select', 'list', 'json'])\n .default('text'),\n options: z.array(z.string()).nullable().default(null),\n itemType: z.enum(['text', 'number']).nullable().default(null),\n }),\n ),\n teamApp: z.boolean(),\n member: z\n .object({\n userId: z.string(),\n email: z.string().nullable(),\n isCaller: z.boolean(),\n })\n .nullable(),\n memberProblem: z.string().nullable(),\n connected: z.boolean(),\n connectedAt: z.string().nullable(),\n connectUrl: z.string(),\n /**\n * Whether the team's product scopes hide the product from it in the app.\n * Absent from an older host.\n */\n productScope: z\n .object({\n state: z.enum(['unscoped', 'scoped', 'locked_out']),\n scopes: z.array(z.string()),\n hidden: z.boolean(),\n })\n .optional(),\n});\nexport type TeamConnectPlan = z.infer<typeof TeamConnectPlanSchema>;\nexport type TeamConnectField = TeamConnectPlan['fields'][number];\n\n/** A field's value as sent: text, or typed by `connect-fields.ts`. */\nexport type FieldValue =\n | string\n | number\n | boolean\n | null\n | FieldValue[]\n | { [key: string]: FieldValue };\n\nconst ShowSchema = z.object({ plan: TeamConnectPlanSchema });\nconst StartSchema = z.discriminatedUnion('result', [\n z.object({ plan: TeamConnectPlanSchema, result: z.literal('activated') }),\n z.object({\n plan: TeamConnectPlanSchema,\n result: z.literal('consent'),\n authorizeUrl: z.string(),\n expiresAt: z.string(),\n }),\n]);\nexport type TeamConnectStart = z.infer<typeof StartSchema>;\n\ninterface Target {\n source: string;\n team: string;\n product: string;\n /** A named OAuth connection (`agent`); the main one when absent. */\n connection?: string;\n}\n\nfunction connectPath(target: Target): string {\n return `/api/partner/teams/${encodeURIComponent(target.team)}/integrations/${encodeURIComponent(target.product)}/connect`;\n}\n\nexport function showTeamConnect(\n host: string,\n accessToken: string,\n target: Target,\n asEmail?: string,\n): Promise<TeamsApiResult<z.infer<typeof ShowSchema>>> {\n // `call` puts `source` in the query; the rest ride in the path's own query.\n const query = new URLSearchParams({\n ...(asEmail ? { as: asEmail } : {}),\n ...(target.connection ? { connection: target.connection } : {}),\n }).toString();\n const path = query ? `${connectPath(target)}?${query}` : connectPath(target);\n return call(host, accessToken, ShowSchema, {\n method: 'GET',\n path,\n source: target.source,\n });\n}\n\nexport function startTeamConnect(\n host: string,\n accessToken: string,\n target: Target,\n body: {\n as?: string;\n fields?: Record<string, FieldValue>;\n /** The field names given by `--set`, not a prompt: the host refuses a secret among them. */\n set?: string[];\n },\n): Promise<TeamsApiResult<TeamConnectStart>> {\n return call(host, accessToken, StartSchema, {\n method: 'POST',\n path: connectPath(target),\n source: target.source,\n body: target.connection ? { ...body, connection: target.connection } : body,\n });\n}\n"]}
|