@better-update/cli 0.27.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1111 -831
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
34
34
|
|
|
35
35
|
//#endregion
|
|
36
36
|
//#region package.json
|
|
37
|
-
var version = "0.
|
|
37
|
+
var version = "0.28.1";
|
|
38
38
|
|
|
39
39
|
//#endregion
|
|
40
40
|
//#region src/lib/interactive-mode.ts
|
|
@@ -527,6 +527,49 @@ var AndroidUploadKeystoresGroup = class extends HttpApiGroup.make("androidUpload
|
|
|
527
527
|
description: "Manage Android signing keystores"
|
|
528
528
|
})) {};
|
|
529
529
|
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region ../../packages/api/src/domain/api-key.ts
|
|
532
|
+
var ApiKey = class extends Schema.Class("ApiKey")({
|
|
533
|
+
id: Id,
|
|
534
|
+
name: Schema.NullOr(Schema.String),
|
|
535
|
+
start: Schema.NullOr(Schema.String),
|
|
536
|
+
prefix: Schema.NullOr(Schema.String),
|
|
537
|
+
enabled: Schema.Boolean,
|
|
538
|
+
createdAt: DateTimeString,
|
|
539
|
+
expiresAt: Schema.NullOr(DateTimeString)
|
|
540
|
+
}) {};
|
|
541
|
+
var CreatedApiKey = class extends Schema.Class("CreatedApiKey")({
|
|
542
|
+
id: Id,
|
|
543
|
+
name: Schema.NullOr(Schema.String),
|
|
544
|
+
start: Schema.NullOr(Schema.String),
|
|
545
|
+
prefix: Schema.NullOr(Schema.String),
|
|
546
|
+
enabled: Schema.Boolean,
|
|
547
|
+
createdAt: DateTimeString,
|
|
548
|
+
expiresAt: Schema.NullOr(DateTimeString),
|
|
549
|
+
key: Schema.String
|
|
550
|
+
}) {};
|
|
551
|
+
const CreateApiKeyBody = Schema.Struct({
|
|
552
|
+
name: Name120,
|
|
553
|
+
expiresInDays: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.positive()))
|
|
554
|
+
});
|
|
555
|
+
const ApiKeyList = Schema.Struct({ items: Schema.Array(ApiKey) });
|
|
556
|
+
|
|
557
|
+
//#endregion
|
|
558
|
+
//#region ../../packages/api/src/groups/api-keys.ts
|
|
559
|
+
var ApiKeysGroup = class extends HttpApiGroup.make("api-keys").add(HttpApiEndpoint.get("list", "/api/api-keys").addSuccess(ApiKeyList).annotateContext(OpenApi.annotations({
|
|
560
|
+
title: "List API keys",
|
|
561
|
+
description: "List the active organization's API keys (hashed secret never exposed; only the `start` prefix for identification)"
|
|
562
|
+
}))).add(HttpApiEndpoint.post("create", "/api/api-keys").setPayload(CreateApiKeyBody).addSuccess(CreatedApiKey, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
563
|
+
title: "Create API key",
|
|
564
|
+
description: "Mint a new API key for the active organization. The plaintext key is returned ONCE; only its hash is stored"
|
|
565
|
+
}))).add(HttpApiEndpoint.del("revoke")`/api/api-keys/${idParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
566
|
+
title: "Revoke API key",
|
|
567
|
+
description: "Delete an API key by id (org-scoped; no cross-organization deletes)"
|
|
568
|
+
}))).addError(NotFound).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
569
|
+
title: "API Keys",
|
|
570
|
+
description: "IAM-gated organization API key mint / list / revoke"
|
|
571
|
+
})) {};
|
|
572
|
+
|
|
530
573
|
//#endregion
|
|
531
574
|
//#region ../../packages/api/src/domain/apple-team.ts
|
|
532
575
|
const AppleTeamType = Schema.Literal("IN_HOUSE", "COMPANY_ORGANIZATION", "INDIVIDUAL");
|
|
@@ -861,7 +904,7 @@ var AssetsGroup = class extends HttpApiGroup.make("assets").add(HttpApiEndpoint.
|
|
|
861
904
|
|
|
862
905
|
//#endregion
|
|
863
906
|
//#region ../../packages/api/src/domain/audit-log.ts
|
|
864
|
-
const AuditLogResourceType = Schema.Literal("project", "branch", "channel", "update", "build", "appleCredential", "androidCredential", "iosBundleConfiguration", "envVar", "device", "webhook", "iosAppMetadata", "submission", "vaultAccess");
|
|
907
|
+
const AuditLogResourceType = Schema.Literal("project", "branch", "channel", "update", "build", "appleCredential", "androidCredential", "iosBundleConfiguration", "envVar", "device", "webhook", "iosAppMetadata", "submission", "vaultAccess", "policy", "group", "policyAttachment", "apiKey", "invitation", "member", "organization");
|
|
865
908
|
const AuditLogSource = Schema.Literal("session", "api-key");
|
|
866
909
|
var AuditLog = class extends Schema.Class("AuditLog")({
|
|
867
910
|
id: Id,
|
|
@@ -1178,44 +1221,6 @@ var BuildsGroup = class extends HttpApiGroup.make("builds").add(HttpApiEndpoint.
|
|
|
1178
1221
|
description: "Build artifact upload, tracking, and download endpoints"
|
|
1179
1222
|
})) {};
|
|
1180
1223
|
|
|
1181
|
-
//#endregion
|
|
1182
|
-
//#region ../../packages/api/src/domain/channel-grant.ts
|
|
1183
|
-
const GrantEffectSchema = Schema.Literal("allow", "deny");
|
|
1184
|
-
/** One member's allow/deny set on a channel; `actions` are "resource:action". */
|
|
1185
|
-
var ChannelGrant = class extends Schema.Class("ChannelGrant")({
|
|
1186
|
-
id: Id,
|
|
1187
|
-
memberId: Id,
|
|
1188
|
-
scopeKind: Schema.Literal("channel"),
|
|
1189
|
-
scopeId: Id,
|
|
1190
|
-
effect: GrantEffectSchema,
|
|
1191
|
-
actions: Schema.Array(Schema.String),
|
|
1192
|
-
createdAt: DateTimeString
|
|
1193
|
-
}) {};
|
|
1194
|
-
/** Upsert one (member, channel, effect) grant. effect defaults to "allow". */
|
|
1195
|
-
const UpsertChannelGrantBody = Schema.Struct({
|
|
1196
|
-
effect: Schema.optionalWith(GrantEffectSchema, { default: () => "allow" }),
|
|
1197
|
-
actions: Schema.Array(Schema.String).pipe(Schema.minItems(1))
|
|
1198
|
-
});
|
|
1199
|
-
const ListChannelGrantsParams = Schema.Struct({});
|
|
1200
|
-
const DeleteChannelGrantResult = Schema.Struct({ deleted: Schema.Number });
|
|
1201
|
-
|
|
1202
|
-
//#endregion
|
|
1203
|
-
//#region ../../packages/api/src/groups/channel-grants.ts
|
|
1204
|
-
const memberIdParam = HttpApiSchema.param("memberId", Schema.String);
|
|
1205
|
-
var ChannelGrantsGroup = class extends HttpApiGroup.make("channelGrants").add(HttpApiEndpoint.get("list")`/api/channels/${idParam}/grants`.setUrlParams(ListChannelGrantsParams).addSuccess(Schema.Array(ChannelGrant)).annotateContext(OpenApi.annotations({
|
|
1206
|
-
title: "List channel grants",
|
|
1207
|
-
description: "List all per-member allow/deny grants on a channel"
|
|
1208
|
-
}))).add(HttpApiEndpoint.put("upsert")`/api/channels/${idParam}/grants/${memberIdParam}`.setPayload(UpsertChannelGrantBody).addSuccess(ChannelGrant).annotateContext(OpenApi.annotations({
|
|
1209
|
-
title: "Upsert channel grant",
|
|
1210
|
-
description: "Create or replace a member's allow/deny grant on a channel"
|
|
1211
|
-
}))).add(HttpApiEndpoint.del("delete")`/api/channels/${idParam}/grants/${memberIdParam}`.addSuccess(DeleteChannelGrantResult).annotateContext(OpenApi.annotations({
|
|
1212
|
-
title: "Delete channel grant",
|
|
1213
|
-
description: "Revoke a member's grants on a channel"
|
|
1214
|
-
}))).addError(NotFound).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
1215
|
-
title: "Channel grants",
|
|
1216
|
-
description: "Per-channel ABAC permission grants (allow/deny by member)"
|
|
1217
|
-
})) {};
|
|
1218
|
-
|
|
1219
1224
|
//#endregion
|
|
1220
1225
|
//#region ../../packages/api/src/domain/channel.ts
|
|
1221
1226
|
var Channel = class extends Schema.Class("Channel")({
|
|
@@ -1471,71 +1476,6 @@ const EnvVarRevision = Schema.Struct({
|
|
|
1471
1476
|
const EnvVarRevisionsResult = Schema.Struct({ items: Schema.Array(EnvVarRevision) });
|
|
1472
1477
|
const RollbackEnvVarBody = Schema.Struct({ toRevisionId: Id });
|
|
1473
1478
|
|
|
1474
|
-
//#endregion
|
|
1475
|
-
//#region ../../packages/api/src/domain/env-grant.ts
|
|
1476
|
-
/**
|
|
1477
|
-
* One member's allow/deny set on a (project × environment) env-var scope.
|
|
1478
|
-
* `scopeKind` is fixed to "env_var_environment". `scopeId` is the encoded
|
|
1479
|
-
* `<projectId|global>:<environment>` token (server-built). `actions` are
|
|
1480
|
-
* "resource:action" tokens (here always envVar:*).
|
|
1481
|
-
*/
|
|
1482
|
-
var EnvGrant = class extends Schema.Class("EnvGrant")({
|
|
1483
|
-
id: Id,
|
|
1484
|
-
memberId: Id,
|
|
1485
|
-
scopeKind: Schema.Literal("env_var_environment"),
|
|
1486
|
-
scopeId: Schema.String,
|
|
1487
|
-
effect: GrantEffectSchema,
|
|
1488
|
-
actions: Schema.Array(Schema.String),
|
|
1489
|
-
createdAt: DateTimeString
|
|
1490
|
-
}) {};
|
|
1491
|
-
/** A flattened row for the list UI: one member × environment cell. */
|
|
1492
|
-
var EnvGrantRow = class extends Schema.Class("EnvGrantRow")({
|
|
1493
|
-
memberId: Id,
|
|
1494
|
-
environment: EnvVarEnvironment,
|
|
1495
|
-
effect: GrantEffectSchema,
|
|
1496
|
-
actions: Schema.Array(Schema.String)
|
|
1497
|
-
}) {};
|
|
1498
|
-
/**
|
|
1499
|
-
* URL params for listing grants on a project-or-global scope. `projectId` is the
|
|
1500
|
-
* sentinel "global" or a real project id (the server resolves null vs the
|
|
1501
|
-
* sentinel). Carried as a query param.
|
|
1502
|
-
*/
|
|
1503
|
-
const ListEnvGrantsParams = Schema.Struct({ projectId: Schema.String });
|
|
1504
|
-
/**
|
|
1505
|
-
* Upsert one (member, project-or-global, environment) grant. `projectId` null =
|
|
1506
|
-
* org-global vault. effect defaults to "allow". actions are envVar:* tokens.
|
|
1507
|
-
*/
|
|
1508
|
-
const UpsertEnvGrantBody = Schema.Struct({
|
|
1509
|
-
memberId: Id,
|
|
1510
|
-
projectId: Schema.NullOr(Id),
|
|
1511
|
-
environment: EnvVarEnvironment,
|
|
1512
|
-
effect: Schema.optionalWith(GrantEffectSchema, { default: () => "allow" }),
|
|
1513
|
-
actions: Schema.Array(Schema.String).pipe(Schema.minItems(1))
|
|
1514
|
-
});
|
|
1515
|
-
/** Delete both effects for (member, project-or-global, environment). */
|
|
1516
|
-
const DeleteEnvGrantBody = Schema.Struct({
|
|
1517
|
-
memberId: Id,
|
|
1518
|
-
projectId: Schema.NullOr(Id),
|
|
1519
|
-
environment: EnvVarEnvironment
|
|
1520
|
-
});
|
|
1521
|
-
const DeleteEnvGrantResult = Schema.Struct({ deleted: Schema.Number });
|
|
1522
|
-
|
|
1523
|
-
//#endregion
|
|
1524
|
-
//#region ../../packages/api/src/groups/env-grants.ts
|
|
1525
|
-
var EnvGrantsGroup = class extends HttpApiGroup.make("envGrants").add(HttpApiEndpoint.get("list", "/api/env-grants").setUrlParams(ListEnvGrantsParams).addSuccess(Schema.Array(EnvGrantRow)).annotateContext(OpenApi.annotations({
|
|
1526
|
-
title: "List env-var environment grants",
|
|
1527
|
-
description: "List per-member allow/deny env-var grants on a project-or-global scope across all environments. projectId is a real id or the sentinel 'global'."
|
|
1528
|
-
}))).add(HttpApiEndpoint.put("upsert", "/api/env-grants").setPayload(UpsertEnvGrantBody).addSuccess(EnvGrant).annotateContext(OpenApi.annotations({
|
|
1529
|
-
title: "Upsert env-var environment grant",
|
|
1530
|
-
description: "Create or replace a member's allow/deny env-var grant on one (project-or-global × environment) scope. projectId null = org-global."
|
|
1531
|
-
}))).add(HttpApiEndpoint.del("delete", "/api/env-grants").setPayload(DeleteEnvGrantBody).addSuccess(DeleteEnvGrantResult).annotateContext(OpenApi.annotations({
|
|
1532
|
-
title: "Delete env-var environment grants",
|
|
1533
|
-
description: "Revoke both allow and deny grants for a member on one (project-or-global × environment) scope."
|
|
1534
|
-
}))).addError(NotFound).addError(Forbidden).addError(BadRequest).annotateContext(OpenApi.annotations({
|
|
1535
|
-
title: "Env-var environment grants",
|
|
1536
|
-
description: "Per (project × environment) ABAC permission grants for env vars (allow/deny by member)"
|
|
1537
|
-
})) {};
|
|
1538
|
-
|
|
1539
1479
|
//#endregion
|
|
1540
1480
|
//#region ../../packages/api/src/groups/env-vars.ts
|
|
1541
1481
|
var EnvVarsGroup = class extends HttpApiGroup.make("env-vars").add(HttpApiEndpoint.post("create", "/api/env-vars").setPayload(CreateEnvVarBody).addSuccess(EnvVar, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
@@ -1760,6 +1700,97 @@ var GoogleServiceAccountKeysGroup = class extends HttpApiGroup.make("googleServi
|
|
|
1760
1700
|
description: "Manage Google Play + FCM service account JSON keys"
|
|
1761
1701
|
})) {};
|
|
1762
1702
|
|
|
1703
|
+
//#endregion
|
|
1704
|
+
//#region ../../packages/api/src/domain/group.ts
|
|
1705
|
+
var Group = class extends Schema.Class("Group")({
|
|
1706
|
+
id: Id,
|
|
1707
|
+
organizationId: Id,
|
|
1708
|
+
name: Schema.NonEmptyString,
|
|
1709
|
+
description: Schema.NullOr(Schema.String),
|
|
1710
|
+
createdAt: DateTimeString,
|
|
1711
|
+
updatedAt: Schema.NullOr(DateTimeString)
|
|
1712
|
+
}) {};
|
|
1713
|
+
const CreateGroupBody = Schema.Struct({
|
|
1714
|
+
name: Schema.NonEmptyString,
|
|
1715
|
+
description: Schema.optional(Schema.String)
|
|
1716
|
+
});
|
|
1717
|
+
const UpdateGroupBody = Schema.Struct({
|
|
1718
|
+
name: Schema.optional(Schema.NonEmptyString),
|
|
1719
|
+
description: Schema.optional(Schema.NullOr(Schema.String))
|
|
1720
|
+
});
|
|
1721
|
+
var GroupMember = class extends Schema.Class("GroupMember")({
|
|
1722
|
+
memberId: Id,
|
|
1723
|
+
createdAt: DateTimeString
|
|
1724
|
+
}) {};
|
|
1725
|
+
const AddGroupMemberBody = Schema.Struct({ memberId: Id });
|
|
1726
|
+
|
|
1727
|
+
//#endregion
|
|
1728
|
+
//#region ../../packages/api/src/groups/groups.ts
|
|
1729
|
+
/** `:memberId` path parameter — the `member.id` of a group member. */
|
|
1730
|
+
const memberIdParam = HttpApiSchema.param("memberId", Id);
|
|
1731
|
+
var GroupsGroup = class extends HttpApiGroup.make("groups").add(HttpApiEndpoint.get("list", "/api/groups").addSuccess(Schema.Struct({ items: Schema.Array(Group) })).annotateContext(OpenApi.annotations({
|
|
1732
|
+
title: "List groups",
|
|
1733
|
+
description: "List member groups in the active organization"
|
|
1734
|
+
}))).add(HttpApiEndpoint.post("create", "/api/groups").setPayload(CreateGroupBody).addSuccess(Group, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
1735
|
+
title: "Create group",
|
|
1736
|
+
description: "Create a member group for the active organization"
|
|
1737
|
+
}))).add(HttpApiEndpoint.get("get")`/api/groups/${idParam}`.addSuccess(Group).annotateContext(OpenApi.annotations({
|
|
1738
|
+
title: "Get group",
|
|
1739
|
+
description: "Fetch a single group by id"
|
|
1740
|
+
}))).add(HttpApiEndpoint.patch("update")`/api/groups/${idParam}`.setPayload(UpdateGroupBody).addSuccess(Group).annotateContext(OpenApi.annotations({
|
|
1741
|
+
title: "Update group",
|
|
1742
|
+
description: "Update a group's name or description"
|
|
1743
|
+
}))).add(HttpApiEndpoint.del("delete")`/api/groups/${idParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
1744
|
+
title: "Delete group",
|
|
1745
|
+
description: "Delete a group and sweep its memberships and policy attachments"
|
|
1746
|
+
}))).add(HttpApiEndpoint.get("listMembers")`/api/groups/${idParam}/members`.addSuccess(Schema.Struct({ items: Schema.Array(GroupMember) })).annotateContext(OpenApi.annotations({
|
|
1747
|
+
title: "List group members",
|
|
1748
|
+
description: "List the members belonging to a group"
|
|
1749
|
+
}))).add(HttpApiEndpoint.post("addMember")`/api/groups/${idParam}/members`.setPayload(AddGroupMemberBody).addSuccess(GroupMember, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
1750
|
+
title: "Add group member",
|
|
1751
|
+
description: "Add an organization member to a group"
|
|
1752
|
+
}))).add(HttpApiEndpoint.del("removeMember")`/api/groups/${idParam}/members/${memberIdParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
1753
|
+
title: "Remove group member",
|
|
1754
|
+
description: "Remove a member from a group"
|
|
1755
|
+
}))).addError(NotFound).addError(Conflict).addError(BadRequest).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
1756
|
+
title: "Groups",
|
|
1757
|
+
description: "Member groups for collective policy attachment"
|
|
1758
|
+
})) {};
|
|
1759
|
+
|
|
1760
|
+
//#endregion
|
|
1761
|
+
//#region ../../packages/api/src/domain/invitation.ts
|
|
1762
|
+
var Invitation = class extends Schema.Class("Invitation")({
|
|
1763
|
+
id: Id,
|
|
1764
|
+
email: Schema.String,
|
|
1765
|
+
role: Schema.NullOr(Schema.String),
|
|
1766
|
+
status: Schema.String,
|
|
1767
|
+
expiresAt: DateTimeString,
|
|
1768
|
+
createdAt: DateTimeString
|
|
1769
|
+
}) {};
|
|
1770
|
+
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
|
|
1771
|
+
const InvitableRole = Schema.Literal("member");
|
|
1772
|
+
const CreateInvitationBody = Schema.Struct({
|
|
1773
|
+
email: Schema.String.pipe(Schema.minLength(1), Schema.maxLength(320), Schema.pattern(EMAIL_PATTERN)),
|
|
1774
|
+
role: Schema.optional(InvitableRole)
|
|
1775
|
+
});
|
|
1776
|
+
const InvitationList = Schema.Struct({ items: Schema.Array(Invitation) });
|
|
1777
|
+
|
|
1778
|
+
//#endregion
|
|
1779
|
+
//#region ../../packages/api/src/groups/invitations.ts
|
|
1780
|
+
var InvitationsGroup = class extends HttpApiGroup.make("invitations").add(HttpApiEndpoint.get("list", "/api/invitations").addSuccess(InvitationList).annotateContext(OpenApi.annotations({
|
|
1781
|
+
title: "List invitations",
|
|
1782
|
+
description: "List the active organization's invitations (all statuses, newest first)"
|
|
1783
|
+
}))).add(HttpApiEndpoint.post("create", "/api/invitations").setPayload(CreateInvitationBody).addSuccess(Invitation, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
1784
|
+
title: "Create invitation",
|
|
1785
|
+
description: "Invite a member to the active organization. Writes a pending `invitation` row (better-auth's accept-invitation consumes it) and sends the invite email"
|
|
1786
|
+
}))).add(HttpApiEndpoint.del("cancel")`/api/invitations/${idParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
1787
|
+
title: "Cancel invitation",
|
|
1788
|
+
description: "Cancel a pending invitation by id (org-scoped). A canceled invitation can no longer be accepted"
|
|
1789
|
+
}))).addError(NotFound).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
1790
|
+
title: "Invitations",
|
|
1791
|
+
description: "IAM-gated organization invitation create / list / cancel"
|
|
1792
|
+
})) {};
|
|
1793
|
+
|
|
1763
1794
|
//#endregion
|
|
1764
1795
|
//#region ../../packages/api/src/domain/ios-app-metadata.ts
|
|
1765
1796
|
const AscAppId = Schema.String.pipe(Schema.pattern(/^[0-9]{1,30}$/u, { message: () => "ASC App ID must be 1-30 digits" }));
|
|
@@ -1896,7 +1927,13 @@ const Me = Schema.Struct({
|
|
|
1896
1927
|
/** Authentication source — "session" for browser + CLI sessions, "api-key" for API-key (CI) bearer tokens. */
|
|
1897
1928
|
source: Schema.Literal("session", "api-key"),
|
|
1898
1929
|
/** Email or descriptor identifying the actor — useful when `user` is null (api-key auth). */
|
|
1899
|
-
actorEmail: Schema.String
|
|
1930
|
+
actorEmail: Schema.String,
|
|
1931
|
+
/** Holds `invitation:create` on `org` — gates the Invite button. */
|
|
1932
|
+
canInviteMembers: Schema.Boolean,
|
|
1933
|
+
/** Holds `member:delete` on `org` — gates the per-member Remove action. */
|
|
1934
|
+
canRemoveMembers: Schema.Boolean,
|
|
1935
|
+
/** Holds `policy:update` on `org` — gates the per-member Manage-policies action. */
|
|
1936
|
+
canManagePolicies: Schema.Boolean
|
|
1900
1937
|
});
|
|
1901
1938
|
|
|
1902
1939
|
//#endregion
|
|
@@ -1910,51 +1947,13 @@ var MeGroup = class extends HttpApiGroup.make("me").add(HttpApiEndpoint.get("get
|
|
|
1910
1947
|
})) {};
|
|
1911
1948
|
|
|
1912
1949
|
//#endregion
|
|
1913
|
-
//#region ../../packages/api/src/
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
actions: Schema.Array(Schema.String)
|
|
1918
|
-
});
|
|
1919
|
-
var OrgRole = class extends Schema.Class("OrgRole")({
|
|
1920
|
-
id: Id,
|
|
1921
|
-
organizationId: Id,
|
|
1922
|
-
role: Schema.String,
|
|
1923
|
-
permissions: Schema.Array(PermissionGrantSchema),
|
|
1924
|
-
createdAt: DateTimeString,
|
|
1925
|
-
updatedAt: Schema.NullOr(DateTimeString)
|
|
1926
|
-
}) {};
|
|
1927
|
-
const CreateOrgRoleBody = Schema.Struct({
|
|
1928
|
-
name: Schema.String.pipe(Schema.minLength(1)),
|
|
1929
|
-
permissions: Schema.Array(PermissionGrantSchema)
|
|
1930
|
-
});
|
|
1931
|
-
const UpdateOrgRoleBody = Schema.Struct({
|
|
1932
|
-
permissions: Schema.optional(Schema.Array(PermissionGrantSchema)),
|
|
1933
|
-
name: Schema.optional(Schema.String.pipe(Schema.minLength(1)))
|
|
1934
|
-
});
|
|
1935
|
-
const ListOrgRolesParams = Schema.Struct({ organizationId: Id });
|
|
1936
|
-
const DeleteOrgRoleResult = Schema.Struct({ deleted: Schema.Number });
|
|
1937
|
-
|
|
1938
|
-
//#endregion
|
|
1939
|
-
//#region ../../packages/api/src/groups/org-roles.ts
|
|
1940
|
-
var OrgRolesGroup = class extends HttpApiGroup.make("roles").add(HttpApiEndpoint.get("list", "/api/roles").setUrlParams(ListOrgRolesParams).addSuccess(Schema.Array(OrgRole)).annotateContext(OpenApi.annotations({
|
|
1941
|
-
title: "List custom roles",
|
|
1942
|
-
description: "List all custom roles defined for an organization"
|
|
1943
|
-
}))).add(HttpApiEndpoint.post("create", "/api/roles").setPayload(CreateOrgRoleBody).addSuccess(OrgRole, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
1944
|
-
title: "Create custom role",
|
|
1945
|
-
description: "Create a new custom role with a permission set"
|
|
1946
|
-
}))).add(HttpApiEndpoint.get("get")`/api/roles/${idParam}`.addSuccess(OrgRole).annotateContext(OpenApi.annotations({
|
|
1947
|
-
title: "Get custom role",
|
|
1948
|
-
description: "Fetch a single custom role by id"
|
|
1949
|
-
}))).add(HttpApiEndpoint.patch("update")`/api/roles/${idParam}`.setPayload(UpdateOrgRoleBody).addSuccess(OrgRole).annotateContext(OpenApi.annotations({
|
|
1950
|
-
title: "Update custom role",
|
|
1951
|
-
description: "Rename a custom role or replace its permission set"
|
|
1952
|
-
}))).add(HttpApiEndpoint.del("delete")`/api/roles/${idParam}`.addSuccess(DeleteOrgRoleResult).annotateContext(OpenApi.annotations({
|
|
1953
|
-
title: "Delete custom role",
|
|
1954
|
-
description: "Delete a custom role"
|
|
1950
|
+
//#region ../../packages/api/src/groups/members.ts
|
|
1951
|
+
var MembersGroup = class extends HttpApiGroup.make("members").add(HttpApiEndpoint.del("remove")`/api/members/${idParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
1952
|
+
title: "Remove member",
|
|
1953
|
+
description: "Remove a member from the active organization by member id (org-scoped; no cross-organization removes). Rejects removing the last owner (409). Membership role is `owner | member`; admin/developer/viewer access comes from policy attachments, not the role"
|
|
1955
1954
|
}))).addError(NotFound).addError(Conflict).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
1956
|
-
title: "
|
|
1957
|
-
description: "
|
|
1955
|
+
title: "Members",
|
|
1956
|
+
description: "IAM-gated organization member removal"
|
|
1958
1957
|
})) {};
|
|
1959
1958
|
|
|
1960
1959
|
//#endregion
|
|
@@ -1987,6 +1986,134 @@ var OrgVaultGroup = class extends HttpApiGroup.make("orgVault").add(HttpApiEndpo
|
|
|
1987
1986
|
description: "Manage the organization's end-to-end encrypted vault key wraps"
|
|
1988
1987
|
})) {};
|
|
1989
1988
|
|
|
1989
|
+
//#endregion
|
|
1990
|
+
//#region ../../packages/api/src/domain/organization.ts
|
|
1991
|
+
var Organization = class extends Schema.Class("Organization")({
|
|
1992
|
+
id: Id,
|
|
1993
|
+
name: Schema.String,
|
|
1994
|
+
slug: Schema.String
|
|
1995
|
+
}) {};
|
|
1996
|
+
const UpdateOrganizationBody = Schema.Struct({
|
|
1997
|
+
name: Schema.optional(Schema.String.pipe(Schema.minLength(1), Schema.maxLength(120))),
|
|
1998
|
+
slug: Schema.optional(Schema.String.pipe(Schema.minLength(1), Schema.maxLength(120)))
|
|
1999
|
+
});
|
|
2000
|
+
|
|
2001
|
+
//#endregion
|
|
2002
|
+
//#region ../../packages/api/src/groups/organization.ts
|
|
2003
|
+
var OrganizationGroup = class extends HttpApiGroup.make("organization").add(HttpApiEndpoint.patch("update", "/api/organization").setPayload(UpdateOrganizationBody).addSuccess(Organization).annotateContext(OpenApi.annotations({
|
|
2004
|
+
title: "Update organization",
|
|
2005
|
+
description: "Rename / re-slug the active organization (IAM-gated by organization:update)"
|
|
2006
|
+
}))).addError(NotFound).addError(Conflict).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
2007
|
+
title: "Organization",
|
|
2008
|
+
description: "IAM-gated active-organization settings"
|
|
2009
|
+
})) {};
|
|
2010
|
+
|
|
2011
|
+
//#endregion
|
|
2012
|
+
//#region ../../packages/api/src/domain/policy.ts
|
|
2013
|
+
/** Whether a statement grants or denies the matched actions. */
|
|
2014
|
+
const PolicyEffect = Schema.Literal("allow", "deny");
|
|
2015
|
+
const PolicyStatement = Schema.Struct({
|
|
2016
|
+
effect: PolicyEffect,
|
|
2017
|
+
actions: Schema.Array(Schema.String).pipe(Schema.minItems(1)),
|
|
2018
|
+
resources: Schema.Array(Schema.String).pipe(Schema.minItems(1))
|
|
2019
|
+
});
|
|
2020
|
+
const PolicyDocument = Schema.Struct({ statements: Schema.Array(PolicyStatement) });
|
|
2021
|
+
var Policy = class extends Schema.Class("Policy")({
|
|
2022
|
+
id: Id,
|
|
2023
|
+
organizationId: Id,
|
|
2024
|
+
name: Schema.NonEmptyString,
|
|
2025
|
+
description: Schema.NullOr(Schema.String),
|
|
2026
|
+
document: PolicyDocument,
|
|
2027
|
+
createdAt: DateTimeString,
|
|
2028
|
+
updatedAt: Schema.NullOr(DateTimeString)
|
|
2029
|
+
}) {};
|
|
2030
|
+
const CreatePolicyBody = Schema.Struct({
|
|
2031
|
+
name: Schema.NonEmptyString,
|
|
2032
|
+
description: Schema.optional(Schema.String),
|
|
2033
|
+
document: PolicyDocument
|
|
2034
|
+
});
|
|
2035
|
+
const UpdatePolicyBody = Schema.Struct({
|
|
2036
|
+
name: Schema.optional(Schema.NonEmptyString),
|
|
2037
|
+
description: Schema.optional(Schema.NullOr(Schema.String)),
|
|
2038
|
+
document: Schema.optional(PolicyDocument)
|
|
2039
|
+
});
|
|
2040
|
+
|
|
2041
|
+
//#endregion
|
|
2042
|
+
//#region ../../packages/api/src/groups/policies.ts
|
|
2043
|
+
var PoliciesGroup = class extends HttpApiGroup.make("policies").add(HttpApiEndpoint.get("list", "/api/policies").addSuccess(Schema.Struct({ items: Schema.Array(Policy) })).annotateContext(OpenApi.annotations({
|
|
2044
|
+
title: "List policies",
|
|
2045
|
+
description: "List policies in the active organization, merging the read-only managed presets (admin/developer/viewer) into the list"
|
|
2046
|
+
}))).add(HttpApiEndpoint.post("create", "/api/policies").setPayload(CreatePolicyBody).addSuccess(Policy, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
2047
|
+
title: "Create policy",
|
|
2048
|
+
description: "Create a named IAM policy document for the active organization"
|
|
2049
|
+
}))).add(HttpApiEndpoint.get("get")`/api/policies/${idParam}`.addSuccess(Policy).annotateContext(OpenApi.annotations({
|
|
2050
|
+
title: "Get policy",
|
|
2051
|
+
description: "Fetch a single policy by id, resolving real ids or managed:* preset ids"
|
|
2052
|
+
}))).add(HttpApiEndpoint.patch("update")`/api/policies/${idParam}`.setPayload(UpdatePolicyBody).addSuccess(Policy).annotateContext(OpenApi.annotations({
|
|
2053
|
+
title: "Update policy",
|
|
2054
|
+
description: "Update a policy's name, description, or document; managed:* ids are rejected"
|
|
2055
|
+
}))).add(HttpApiEndpoint.del("delete")`/api/policies/${idParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
2056
|
+
title: "Delete policy",
|
|
2057
|
+
description: "Delete a policy and sweep its attachments; managed:* ids are rejected"
|
|
2058
|
+
}))).addError(NotFound).addError(Conflict).addError(BadRequest).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
2059
|
+
title: "Policies",
|
|
2060
|
+
description: "IAM policy documents (named, reusable permission grants)"
|
|
2061
|
+
})) {};
|
|
2062
|
+
|
|
2063
|
+
//#endregion
|
|
2064
|
+
//#region ../../packages/api/src/domain/policy-attachment.ts
|
|
2065
|
+
/** Whether an attachment binds a policy to a member, a group, or an api-key. */
|
|
2066
|
+
const PrincipalType = Schema.Literal("member", "group", "apikey");
|
|
2067
|
+
var PolicyAttachment = class extends Schema.Class("PolicyAttachment")({
|
|
2068
|
+
id: Id,
|
|
2069
|
+
organizationId: Id,
|
|
2070
|
+
policyId: Schema.String,
|
|
2071
|
+
principalType: PrincipalType,
|
|
2072
|
+
principalId: Id,
|
|
2073
|
+
createdAt: DateTimeString
|
|
2074
|
+
}) {};
|
|
2075
|
+
const AttachPolicyBody = Schema.Struct({ policyId: Schema.String });
|
|
2076
|
+
|
|
2077
|
+
//#endregion
|
|
2078
|
+
//#region ../../packages/api/src/groups/policy-attachments.ts
|
|
2079
|
+
/**
|
|
2080
|
+
* `:policyId` path parameter — a real `policy.id` or a managed preset id. Managed
|
|
2081
|
+
* ids contain a colon (`managed:admin`); the single path segment matches it as-is,
|
|
2082
|
+
* and clients URL-encode the colon when building the path.
|
|
2083
|
+
*/
|
|
2084
|
+
const policyIdParam = HttpApiSchema.param("policyId", Schema.String);
|
|
2085
|
+
var PolicyAttachmentsGroup = class extends HttpApiGroup.make("policy-attachments").add(HttpApiEndpoint.get("listForMember")`/api/members/${idParam}/policies`.addSuccess(Schema.Struct({ items: Schema.Array(PolicyAttachment) })).annotateContext(OpenApi.annotations({
|
|
2086
|
+
title: "List member policy attachments",
|
|
2087
|
+
description: "List policies attached directly to an organization member"
|
|
2088
|
+
}))).add(HttpApiEndpoint.post("attachToMember")`/api/members/${idParam}/policies`.setPayload(AttachPolicyBody).addSuccess(PolicyAttachment, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
2089
|
+
title: "Attach policy to member",
|
|
2090
|
+
description: "Attach a policy (real or managed) directly to a member"
|
|
2091
|
+
}))).add(HttpApiEndpoint.del("detachFromMember")`/api/members/${idParam}/policies/${policyIdParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
2092
|
+
title: "Detach policy from member",
|
|
2093
|
+
description: "Remove a policy attachment from a member"
|
|
2094
|
+
}))).add(HttpApiEndpoint.get("listForGroup")`/api/groups/${idParam}/policies`.addSuccess(Schema.Struct({ items: Schema.Array(PolicyAttachment) })).annotateContext(OpenApi.annotations({
|
|
2095
|
+
title: "List group policy attachments",
|
|
2096
|
+
description: "List policies attached to a group"
|
|
2097
|
+
}))).add(HttpApiEndpoint.post("attachToGroup")`/api/groups/${idParam}/policies`.setPayload(AttachPolicyBody).addSuccess(PolicyAttachment, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
2098
|
+
title: "Attach policy to group",
|
|
2099
|
+
description: "Attach a policy (real or managed) to a group; members inherit it"
|
|
2100
|
+
}))).add(HttpApiEndpoint.del("detachFromGroup")`/api/groups/${idParam}/policies/${policyIdParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
2101
|
+
title: "Detach policy from group",
|
|
2102
|
+
description: "Remove a policy attachment from a group"
|
|
2103
|
+
}))).add(HttpApiEndpoint.get("listForApiKey")`/api/api-keys/${idParam}/policies`.addSuccess(Schema.Struct({ items: Schema.Array(PolicyAttachment) })).annotateContext(OpenApi.annotations({
|
|
2104
|
+
title: "List api-key policy attachments",
|
|
2105
|
+
description: "List policies attached to an api-key principal"
|
|
2106
|
+
}))).add(HttpApiEndpoint.post("attachToApiKey")`/api/api-keys/${idParam}/policies`.setPayload(AttachPolicyBody).addSuccess(PolicyAttachment, { status: 201 }).annotateContext(OpenApi.annotations({
|
|
2107
|
+
title: "Attach policy to api-key",
|
|
2108
|
+
description: "Attach a policy (real or managed) to an api-key principal"
|
|
2109
|
+
}))).add(HttpApiEndpoint.del("detachFromApiKey")`/api/api-keys/${idParam}/policies/${policyIdParam}`.addSuccess(DeletedResult).annotateContext(OpenApi.annotations({
|
|
2110
|
+
title: "Detach policy from api-key",
|
|
2111
|
+
description: "Remove a policy attachment from an api-key principal"
|
|
2112
|
+
}))).addError(NotFound).addError(Conflict).addError(BadRequest).addError(Forbidden).annotateContext(OpenApi.annotations({
|
|
2113
|
+
title: "Policy Attachments",
|
|
2114
|
+
description: "Bindings of policies to member, group, and api-key principals"
|
|
2115
|
+
})) {};
|
|
2116
|
+
|
|
1990
2117
|
//#endregion
|
|
1991
2118
|
//#region ../../packages/api/src/domain/project.ts
|
|
1992
2119
|
var Project = class extends Schema.Class("Project")({
|
|
@@ -2307,7 +2434,7 @@ var WebhooksGroup = class extends HttpApiGroup.make("webhooks").add(HttpApiEndpo
|
|
|
2307
2434
|
|
|
2308
2435
|
//#endregion
|
|
2309
2436
|
//#region ../../packages/api/src/api.ts
|
|
2310
|
-
var ManagementApi = class extends HttpApi.make("management-api").add(ProjectsGroup).add(BranchesGroup).add(ChannelsGroup).add(UpdatesGroup).add(AssetsGroup).add(AnalyticsGroup).add(BuildsGroup).add(EnvVarsGroup).add(FingerprintsGroup).add(AuditLogsGroup).add(DevicesGroup).add(AppleTeamsGroup).add(AppleDistributionCertificatesGroup).add(ApplePushKeysGroup).add(AscApiKeysGroup).add(AppleProvisioningProfilesGroup).add(GoogleServiceAccountKeysGroup).add(IosBundleConfigurationsGroup).add(IosAppMetadataGroup).add(SubmissionsGroup).add(AndroidApplicationIdentifiersGroup).add(AndroidUploadKeystoresGroup).add(AndroidBuildCredentialsGroup).add(BuildCredentialsGroup).add(UserEncryptionKeysGroup).add(OrgVaultGroup).add(MeGroup).add(WebhooksGroup).add(
|
|
2437
|
+
var ManagementApi = class extends HttpApi.make("management-api").add(ProjectsGroup).add(BranchesGroup).add(ChannelsGroup).add(UpdatesGroup).add(AssetsGroup).add(AnalyticsGroup).add(BuildsGroup).add(EnvVarsGroup).add(FingerprintsGroup).add(AuditLogsGroup).add(DevicesGroup).add(AppleTeamsGroup).add(AppleDistributionCertificatesGroup).add(ApplePushKeysGroup).add(AscApiKeysGroup).add(AppleProvisioningProfilesGroup).add(GoogleServiceAccountKeysGroup).add(IosBundleConfigurationsGroup).add(IosAppMetadataGroup).add(SubmissionsGroup).add(AndroidApplicationIdentifiersGroup).add(AndroidUploadKeystoresGroup).add(AndroidBuildCredentialsGroup).add(BuildCredentialsGroup).add(UserEncryptionKeysGroup).add(OrgVaultGroup).add(MeGroup).add(WebhooksGroup).add(PoliciesGroup).add(GroupsGroup).add(PolicyAttachmentsGroup).add(ApiKeysGroup).add(InvitationsGroup).add(MembersGroup).add(OrganizationGroup).add(AdminGroup).middleware(Authentication).annotateContext(OpenApi.annotations({
|
|
2311
2438
|
title: "Better Update Management API",
|
|
2312
2439
|
version: "1.0.0",
|
|
2313
2440
|
description: "Management API for OTA update publishing, deployment, and analytics"
|
|
@@ -2337,6 +2464,151 @@ var ProtocolApi = class extends HttpApi.make("protocol-api").add(ManifestGroup).
|
|
|
2337
2464
|
description: "Expo Updates protocol endpoints (unauthenticated)"
|
|
2338
2465
|
})) {};
|
|
2339
2466
|
|
|
2467
|
+
//#endregion
|
|
2468
|
+
//#region ../../packages/api/src/domain/policy-selector.ts
|
|
2469
|
+
/**
|
|
2470
|
+
* Pure, framework-agnostic validators for the IAM path-glob SELECTOR GRAMMAR and
|
|
2471
|
+
* the action-token shape. Shared by web / CLI / server so all three reject bad
|
|
2472
|
+
* input identically. This is INPUT-SHAPE validation only — it is distinct from
|
|
2473
|
+
* the server matching algorithm (`selectorMatches`), which is NOT duplicated here.
|
|
2474
|
+
*/
|
|
2475
|
+
/** A selector segment: `*` or a non-empty token of `[A-Za-z0-9._:-]`. */
|
|
2476
|
+
const SEGMENT_PATTERN = /^[A-Za-z0-9._:-]+$/u;
|
|
2477
|
+
/**
|
|
2478
|
+
* An action token of `"<word>:<word>"` or `"<word>:*"`, where a word is a
|
|
2479
|
+
* non-empty run of `[A-Za-z0-9_-]`. The standalone `"*"` token is handled
|
|
2480
|
+
* separately in `isValidActionTokenShape`.
|
|
2481
|
+
*/
|
|
2482
|
+
const ACTION_TOKEN_PATTERN = /^[A-Za-z0-9_-]+:(?:\*|[A-Za-z0-9_-]+)$/u;
|
|
2483
|
+
const isValidSegment = (segment) => segment === "*" || SEGMENT_PATTERN.test(segment);
|
|
2484
|
+
/**
|
|
2485
|
+
* A resource selector is valid when it is `"*"` OR is slash-joined segments where
|
|
2486
|
+
* each segment is `"*"` or a non-empty token of `[A-Za-z0-9._:-]`. Empty segments
|
|
2487
|
+
* (leading/trailing/double slashes) are rejected.
|
|
2488
|
+
*/
|
|
2489
|
+
const isValidSelector = (selector) => {
|
|
2490
|
+
if (selector === "*") return true;
|
|
2491
|
+
if (selector.length === 0) return false;
|
|
2492
|
+
return selector.split("/").every(isValidSegment);
|
|
2493
|
+
};
|
|
2494
|
+
/**
|
|
2495
|
+
* An action token is valid in SHAPE when it is `"*"`, `"<word>:<word>"`, or
|
|
2496
|
+
* `"<word>:*"`. The server still validates the token against the real
|
|
2497
|
+
* resource/action vocabulary — this only guards the grammar.
|
|
2498
|
+
*/
|
|
2499
|
+
const isValidActionTokenShape = (token) => token === "*" || ACTION_TOKEN_PATTERN.test(token);
|
|
2500
|
+
const ID = "@";
|
|
2501
|
+
const CANONICAL_TEMPLATES = [
|
|
2502
|
+
["org"],
|
|
2503
|
+
["project", ID],
|
|
2504
|
+
[
|
|
2505
|
+
"project",
|
|
2506
|
+
ID,
|
|
2507
|
+
"build"
|
|
2508
|
+
],
|
|
2509
|
+
[
|
|
2510
|
+
"project",
|
|
2511
|
+
ID,
|
|
2512
|
+
"build",
|
|
2513
|
+
ID
|
|
2514
|
+
],
|
|
2515
|
+
[
|
|
2516
|
+
"project",
|
|
2517
|
+
ID,
|
|
2518
|
+
"credential"
|
|
2519
|
+
],
|
|
2520
|
+
[
|
|
2521
|
+
"project",
|
|
2522
|
+
ID,
|
|
2523
|
+
"credential",
|
|
2524
|
+
ID
|
|
2525
|
+
],
|
|
2526
|
+
[
|
|
2527
|
+
"project",
|
|
2528
|
+
ID,
|
|
2529
|
+
"submission"
|
|
2530
|
+
],
|
|
2531
|
+
[
|
|
2532
|
+
"project",
|
|
2533
|
+
ID,
|
|
2534
|
+
"submission",
|
|
2535
|
+
ID
|
|
2536
|
+
],
|
|
2537
|
+
[
|
|
2538
|
+
"project",
|
|
2539
|
+
ID,
|
|
2540
|
+
"env",
|
|
2541
|
+
ID
|
|
2542
|
+
],
|
|
2543
|
+
[
|
|
2544
|
+
"project",
|
|
2545
|
+
ID,
|
|
2546
|
+
"env",
|
|
2547
|
+
ID,
|
|
2548
|
+
"envVar"
|
|
2549
|
+
],
|
|
2550
|
+
[
|
|
2551
|
+
"project",
|
|
2552
|
+
ID,
|
|
2553
|
+
"env",
|
|
2554
|
+
ID,
|
|
2555
|
+
"envVar",
|
|
2556
|
+
ID
|
|
2557
|
+
],
|
|
2558
|
+
[
|
|
2559
|
+
"project",
|
|
2560
|
+
ID,
|
|
2561
|
+
"channel",
|
|
2562
|
+
ID
|
|
2563
|
+
],
|
|
2564
|
+
[
|
|
2565
|
+
"project",
|
|
2566
|
+
ID,
|
|
2567
|
+
"channel",
|
|
2568
|
+
ID,
|
|
2569
|
+
"update"
|
|
2570
|
+
],
|
|
2571
|
+
[
|
|
2572
|
+
"project",
|
|
2573
|
+
ID,
|
|
2574
|
+
"channel",
|
|
2575
|
+
ID,
|
|
2576
|
+
"update",
|
|
2577
|
+
ID
|
|
2578
|
+
],
|
|
2579
|
+
[
|
|
2580
|
+
"project",
|
|
2581
|
+
ID,
|
|
2582
|
+
"channel",
|
|
2583
|
+
ID,
|
|
2584
|
+
"rollout"
|
|
2585
|
+
],
|
|
2586
|
+
[
|
|
2587
|
+
"project",
|
|
2588
|
+
ID,
|
|
2589
|
+
"channel",
|
|
2590
|
+
ID,
|
|
2591
|
+
"rollout",
|
|
2592
|
+
ID
|
|
2593
|
+
]
|
|
2594
|
+
];
|
|
2595
|
+
const matchesTemplate = (template, segments) => template.length === segments.length && template.every((slot, index) => {
|
|
2596
|
+
const segment = segments[index];
|
|
2597
|
+
return segment !== void 0 && (slot === ID || segment === slot || segment === "*");
|
|
2598
|
+
});
|
|
2599
|
+
/**
|
|
2600
|
+
* True when a (shape-valid) selector matches one of the canonical resource-path
|
|
2601
|
+
* templates the server can actually produce — so a typo'd or pluralised segment
|
|
2602
|
+
* (e.g. `"project/A/channels/X"`) is caught at policy-write time instead of being
|
|
2603
|
+
* stored as a silently inert policy that can never match. The standalone `"*"`
|
|
2604
|
+
* matches everything and is always canonical.
|
|
2605
|
+
*/
|
|
2606
|
+
const isCanonicalSelector = (selector) => {
|
|
2607
|
+
if (selector === "*") return true;
|
|
2608
|
+
const segments = selector.split("/");
|
|
2609
|
+
return CANONICAL_TEMPLATES.some((template) => matchesTemplate(template, segments));
|
|
2610
|
+
};
|
|
2611
|
+
|
|
2340
2612
|
//#endregion
|
|
2341
2613
|
//#region src/lib/exit-codes.ts
|
|
2342
2614
|
var AuthRequiredError = class extends Data.TaggedError("AuthRequiredError") {};
|
|
@@ -2373,8 +2645,8 @@ var FingerprintMismatchError = class extends Data.TaggedError("FingerprintMismat
|
|
|
2373
2645
|
|
|
2374
2646
|
//#endregion
|
|
2375
2647
|
//#region ../../packages/type-guards/src/index.ts
|
|
2376
|
-
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2377
|
-
const asRecord = (value) => isRecord(value) ? value : void 0;
|
|
2648
|
+
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2649
|
+
const asRecord = (value) => isRecord$1(value) ? value : void 0;
|
|
2378
2650
|
const toOptional = (value) => value ?? void 0;
|
|
2379
2651
|
const toDbNull = (value) => value ?? null;
|
|
2380
2652
|
const compact = (obj) => Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== void 0));
|
|
@@ -2432,7 +2704,7 @@ const AuthStoreLive = Layer.effect(AuthStore, Effect.gen(function* () {
|
|
|
2432
2704
|
try: () => JSON.parse(content),
|
|
2433
2705
|
catch: () => new AuthRequiredError({ message: "Corrupted auth file. Run `better-update login` to re-authenticate." })
|
|
2434
2706
|
});
|
|
2435
|
-
if (!isRecord(parsed)) return yield* new AuthRequiredError({ message: "Invalid auth file. Run `better-update login` to re-authenticate." });
|
|
2707
|
+
if (!isRecord$1(parsed)) return yield* new AuthRequiredError({ message: "Invalid auth file. Run `better-update login` to re-authenticate." });
|
|
2436
2708
|
const { token } = parsed;
|
|
2437
2709
|
if (typeof token !== "string") return yield* new AuthRequiredError({ message: "Invalid auth file. Run `better-update login` to re-authenticate." });
|
|
2438
2710
|
return token;
|
|
@@ -2466,7 +2738,7 @@ const ConfigStoreLive = Layer.effect(ConfigStore, Effect.gen(function* () {
|
|
|
2466
2738
|
message: "Config file contains invalid JSON",
|
|
2467
2739
|
cause
|
|
2468
2740
|
})
|
|
2469
|
-
}).pipe(Effect.map((parsed) => isRecord(parsed) ? parsed : void 0), Effect.orElseSucceed(() => void 0))));
|
|
2741
|
+
}).pipe(Effect.map((parsed) => isRecord$1(parsed) ? parsed : void 0), Effect.orElseSucceed(() => void 0))));
|
|
2470
2742
|
return {
|
|
2471
2743
|
getBaseUrl: Effect.gen(function* () {
|
|
2472
2744
|
const envUrl = yield* runtime.getEnv("BETTER_UPDATE_URL");
|
|
@@ -2692,7 +2964,7 @@ const AppleSessionStoreLive = Layer.effect(AppleSessionStore, Effect.gen(functio
|
|
|
2692
2964
|
const content = yield* fs.readFileString(sessionFile).pipe(Effect.orElseSucceed(() => null));
|
|
2693
2965
|
if (!content) return null;
|
|
2694
2966
|
const parsed = safeJsonParse(content);
|
|
2695
|
-
if (!isRecord(parsed)) return null;
|
|
2967
|
+
if (!isRecord$1(parsed)) return null;
|
|
2696
2968
|
if (typeof parsed["username"] !== "string" || !parsed["cookies"]) return null;
|
|
2697
2969
|
return {
|
|
2698
2970
|
cookies: parsed["cookies"],
|
|
@@ -2710,7 +2982,7 @@ const AppleSessionStoreLive = Layer.effect(AppleSessionStore, Effect.gen(functio
|
|
|
2710
2982
|
const content = yield* fs.readFileString(usernameFile).pipe(Effect.orElseSucceed(() => null));
|
|
2711
2983
|
if (!content) return null;
|
|
2712
2984
|
const parsed = safeJsonParse(content);
|
|
2713
|
-
if (!isRecord(parsed) || typeof parsed["username"] !== "string") return null;
|
|
2985
|
+
if (!isRecord$1(parsed) || typeof parsed["username"] !== "string") return null;
|
|
2714
2986
|
return parsed["username"];
|
|
2715
2987
|
}),
|
|
2716
2988
|
saveLastUsername: (username) => Effect.gen(function* () {
|
|
@@ -2893,13 +3165,13 @@ const BsdiffServiceLive = Layer.succeed(BsdiffService, { diff: (input) => Effect
|
|
|
2893
3165
|
|
|
2894
3166
|
//#endregion
|
|
2895
3167
|
//#region src/services/identity-store.ts
|
|
2896
|
-
const isArgon2Params = (value) => isRecord(value) && typeof value["time"] === "number" && typeof value["memory"] === "number" && typeof value["parallelism"] === "number";
|
|
3168
|
+
const isArgon2Params = (value) => isRecord$1(value) && typeof value["time"] === "number" && typeof value["memory"] === "number" && typeof value["parallelism"] === "number";
|
|
2897
3169
|
/**
|
|
2898
3170
|
* Structural guard for the on-disk identity envelope. A corrupt or foreign file
|
|
2899
3171
|
* reads as "absent" so the CLI prompts to (re)create rather than crashing — the
|
|
2900
3172
|
* AAD-bound `openIdentity` still fails loudly if a well-formed file was tampered.
|
|
2901
3173
|
*/
|
|
2902
|
-
const isIdentityFile = (value) => isRecord(value) && value["version"] === 1 && typeof value["publicKey"] === "string" && typeof value["fingerprint"] === "string" && value["kdf"] === "argon2id" && isArgon2Params(value["kdfParams"]) && typeof value["salt"] === "string" && value["cipher"] === "xchacha20poly1305" && typeof value["ct"] === "string";
|
|
3174
|
+
const isIdentityFile = (value) => isRecord$1(value) && value["version"] === 1 && typeof value["publicKey"] === "string" && typeof value["fingerprint"] === "string" && value["kdf"] === "argon2id" && isArgon2Params(value["kdfParams"]) && typeof value["salt"] === "string" && value["cipher"] === "xchacha20poly1305" && typeof value["ct"] === "string";
|
|
2903
3175
|
var IdentityStore = class extends Context.Tag("cli/IdentityStore")() {};
|
|
2904
3176
|
const IdentityStoreLive = Layer.effect(IdentityStore, Effect.gen(function* () {
|
|
2905
3177
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -3100,7 +3372,7 @@ const UpdateAssetUploaderLive = Layer.effect(UpdateAssetUploader, Effect.gen(fun
|
|
|
3100
3372
|
const VAULT_CACHE_TTL_MS = 900 * 1e3;
|
|
3101
3373
|
/** Keychain service name; the account is the recipient's public key. */
|
|
3102
3374
|
const KEYCHAIN_SERVICE = "better-update-vault";
|
|
3103
|
-
const isCachedVaultEntry = (value) => isRecord(value) && typeof value["vaultKey"] === "string" && typeof value["vaultVersion"] === "number" && typeof value["keyId"] === "string" && typeof value["exp"] === "number";
|
|
3375
|
+
const isCachedVaultEntry = (value) => isRecord$1(value) && typeof value["vaultKey"] === "string" && typeof value["vaultVersion"] === "number" && typeof value["keyId"] === "string" && typeof value["exp"] === "number";
|
|
3104
3376
|
/** Serialize an unlocked vault into a keychain blob, stamping a TTL from `now`. */
|
|
3105
3377
|
const encodeCacheEntry = (vault, now, ttlMs = VAULT_CACHE_TTL_MS) => JSON.stringify({
|
|
3106
3378
|
vaultKey: toBase64(vault.vaultKey),
|
|
@@ -3176,7 +3448,7 @@ const VersionCheckLive = Layer.effect(VersionCheck, Effect.gen(function* () {
|
|
|
3176
3448
|
try: () => JSON.parse(content),
|
|
3177
3449
|
catch: () => "parse-error"
|
|
3178
3450
|
}).pipe(Effect.orElseSucceed(() => void 0));
|
|
3179
|
-
if (isRecord(parsed) && typeof parsed["latest"] === "string" && typeof parsed["checkedAt"] === "number") return {
|
|
3451
|
+
if (isRecord$1(parsed) && typeof parsed["latest"] === "string" && typeof parsed["checkedAt"] === "number") return {
|
|
3180
3452
|
latest: parsed["latest"],
|
|
3181
3453
|
checkedAt: parsed["checkedAt"]
|
|
3182
3454
|
};
|
|
@@ -3193,7 +3465,7 @@ const VersionCheckLive = Layer.effect(VersionCheck, Effect.gen(function* () {
|
|
|
3193
3465
|
const response = yield* httpClient.execute(request);
|
|
3194
3466
|
if (response.status < 200 || response.status >= 300) return;
|
|
3195
3467
|
const body = yield* response.json;
|
|
3196
|
-
if (!isRecord(body) || typeof body["version"] !== "string") return;
|
|
3468
|
+
if (!isRecord$1(body) || typeof body["version"] !== "string") return;
|
|
3197
3469
|
const latest = body["version"];
|
|
3198
3470
|
yield* fs.makeDirectory(cacheDir, { recursive: true });
|
|
3199
3471
|
yield* fs.writeFileString(cacheFile, `${JSON.stringify({
|
|
@@ -3294,7 +3566,7 @@ const createBrowserLoginSession = (options = {}) => {
|
|
|
3294
3566
|
if (request.method === "GET" && url.pathname === "/callback") return new Response(CALLBACK_PAGE, { headers: { "content-type": "text/html; charset=utf-8" } });
|
|
3295
3567
|
if (request.method === "POST" && url.pathname === "/callback/token") try {
|
|
3296
3568
|
const body = await request.json();
|
|
3297
|
-
if (!isRecord(body)) return new Response("Invalid callback payload", { status: 400 });
|
|
3569
|
+
if (!isRecord$1(body)) return new Response("Invalid callback payload", { status: 400 });
|
|
3298
3570
|
const token = typeof body["token"] === "string" ? body["token"].trim() : "";
|
|
3299
3571
|
if (token.length === 0) return new Response("Missing token", { status: 400 });
|
|
3300
3572
|
Effect.runSync(Deferred.succeed(tokenDeferred, token));
|
|
@@ -3777,7 +4049,7 @@ const configPath = (projectRoot) => path.join(projectRoot, BETTER_UPDATE_CONFIG_
|
|
|
3777
4049
|
const readBetterUpdateConfig = (projectRoot) => Effect.gen(function* () {
|
|
3778
4050
|
const content = yield* (yield* FileSystem.FileSystem).readFileString(configPath(projectRoot)).pipe(Effect.orElseSucceed(() => ""));
|
|
3779
4051
|
if (content.length === 0) return;
|
|
3780
|
-
return yield* Effect.try(() => JSON.parse(content)).pipe(Effect.map((parsed) => isRecord(parsed) ? parsed : void 0), Effect.orElseSucceed(() => void 0));
|
|
4052
|
+
return yield* Effect.try(() => JSON.parse(content)).pipe(Effect.map((parsed) => isRecord$1(parsed) ? parsed : void 0), Effect.orElseSucceed(() => void 0));
|
|
3781
4053
|
});
|
|
3782
4054
|
/**
|
|
3783
4055
|
* Resolve the linked project id from `better-update.json`, or `undefined` when
|
|
@@ -4517,7 +4789,7 @@ const parseLimit = (raw, defaultValue) => {
|
|
|
4517
4789
|
|
|
4518
4790
|
//#endregion
|
|
4519
4791
|
//#region src/commands/audit-logs/list.ts
|
|
4520
|
-
const listCommand$
|
|
4792
|
+
const listCommand$9 = defineCommand({
|
|
4521
4793
|
meta: {
|
|
4522
4794
|
name: "list",
|
|
4523
4795
|
description: "List audit log entries"
|
|
@@ -4579,7 +4851,7 @@ const auditLogsCommand = defineCommand({
|
|
|
4579
4851
|
name: "audit-logs",
|
|
4580
4852
|
description: "View audit logs"
|
|
4581
4853
|
},
|
|
4582
|
-
subCommands: { list: listCommand$
|
|
4854
|
+
subCommands: { list: listCommand$9 }
|
|
4583
4855
|
});
|
|
4584
4856
|
|
|
4585
4857
|
//#endregion
|
|
@@ -4683,7 +4955,7 @@ const drainPages = (fetchPage) => {
|
|
|
4683
4955
|
|
|
4684
4956
|
//#endregion
|
|
4685
4957
|
//#region src/commands/branches.ts
|
|
4686
|
-
const listCommand$
|
|
4958
|
+
const listCommand$8 = defineCommand({
|
|
4687
4959
|
meta: {
|
|
4688
4960
|
name: "list",
|
|
4689
4961
|
description: "List branches for the linked project"
|
|
@@ -4706,7 +4978,7 @@ const listCommand$11 = defineCommand({
|
|
|
4706
4978
|
]), "No branches found.");
|
|
4707
4979
|
}))
|
|
4708
4980
|
});
|
|
4709
|
-
const createCommand$
|
|
4981
|
+
const createCommand$4 = defineCommand({
|
|
4710
4982
|
meta: {
|
|
4711
4983
|
name: "create",
|
|
4712
4984
|
description: "Create a branch"
|
|
@@ -4729,7 +5001,7 @@ const createCommand$5 = defineCommand({
|
|
|
4729
5001
|
]);
|
|
4730
5002
|
}))
|
|
4731
5003
|
});
|
|
4732
|
-
const viewCommand$
|
|
5004
|
+
const viewCommand$3 = defineCommand({
|
|
4733
5005
|
meta: {
|
|
4734
5006
|
name: "view",
|
|
4735
5007
|
description: "Show a branch by ID or name"
|
|
@@ -4787,7 +5059,7 @@ const renameCommand$1 = defineCommand({
|
|
|
4787
5059
|
return branch;
|
|
4788
5060
|
}), { json: "value" })
|
|
4789
5061
|
});
|
|
4790
|
-
const deleteCommand$
|
|
5062
|
+
const deleteCommand$6 = defineCommand({
|
|
4791
5063
|
meta: {
|
|
4792
5064
|
name: "delete",
|
|
4793
5065
|
description: "Delete a branch"
|
|
@@ -4812,11 +5084,11 @@ const branchesCommand = defineCommand({
|
|
|
4812
5084
|
description: "Manage branches"
|
|
4813
5085
|
},
|
|
4814
5086
|
subCommands: {
|
|
4815
|
-
list: listCommand$
|
|
4816
|
-
view: viewCommand$
|
|
4817
|
-
create: createCommand$
|
|
5087
|
+
list: listCommand$8,
|
|
5088
|
+
view: viewCommand$3,
|
|
5089
|
+
create: createCommand$4,
|
|
4818
5090
|
rename: renameCommand$1,
|
|
4819
|
-
delete: deleteCommand$
|
|
5091
|
+
delete: deleteCommand$6
|
|
4820
5092
|
}
|
|
4821
5093
|
});
|
|
4822
5094
|
|
|
@@ -18906,8 +19178,8 @@ var AscApiError = class extends Data.TaggedError("AscApiError") {};
|
|
|
18906
19178
|
var AscNetworkError = class extends Data.TaggedError("AscNetworkError") {};
|
|
18907
19179
|
const API_BASE = "https://api.appstoreconnect.apple.com";
|
|
18908
19180
|
const extractErrors = (body) => {
|
|
18909
|
-
if (!isRecord(body) || !Array.isArray(body["errors"])) return [];
|
|
18910
|
-
return body["errors"].filter((value) => isRecord(value));
|
|
19181
|
+
if (!isRecord$1(body) || !Array.isArray(body["errors"])) return [];
|
|
19182
|
+
return body["errors"].filter((value) => isRecord$1(value));
|
|
18911
19183
|
};
|
|
18912
19184
|
const parseApiError = (response, body, raw) => {
|
|
18913
19185
|
const [first] = extractErrors(body);
|
|
@@ -18943,9 +19215,9 @@ const fetchRaw = (jwt, path, init) => Effect.gen(function* () {
|
|
|
18943
19215
|
return body;
|
|
18944
19216
|
});
|
|
18945
19217
|
const toAscCertificate = (value) => {
|
|
18946
|
-
if (!isRecord(value)) return null;
|
|
19218
|
+
if (!isRecord$1(value)) return null;
|
|
18947
19219
|
const { id, attributes } = value;
|
|
18948
|
-
if (typeof id !== "string" || !isRecord(attributes)) return null;
|
|
19220
|
+
if (typeof id !== "string" || !isRecord$1(attributes)) return null;
|
|
18949
19221
|
const { serialNumber, certificateType, expirationDate, certificateContent, displayName } = attributes;
|
|
18950
19222
|
if (typeof serialNumber !== "string" || typeof certificateType !== "string" || typeof expirationDate !== "string") return null;
|
|
18951
19223
|
return {
|
|
@@ -18958,9 +19230,9 @@ const toAscCertificate = (value) => {
|
|
|
18958
19230
|
};
|
|
18959
19231
|
};
|
|
18960
19232
|
const toAscBundleId = (value) => {
|
|
18961
|
-
if (!isRecord(value)) return null;
|
|
19233
|
+
if (!isRecord$1(value)) return null;
|
|
18962
19234
|
const { id, attributes } = value;
|
|
18963
|
-
if (typeof id !== "string" || !isRecord(attributes)) return null;
|
|
19235
|
+
if (typeof id !== "string" || !isRecord$1(attributes)) return null;
|
|
18964
19236
|
const { identifier, name } = attributes;
|
|
18965
19237
|
if (typeof identifier !== "string" || typeof name !== "string") return null;
|
|
18966
19238
|
return {
|
|
@@ -18980,9 +19252,9 @@ const asProfileType = (value) => {
|
|
|
18980
19252
|
return match === void 0 ? null : match;
|
|
18981
19253
|
};
|
|
18982
19254
|
const toAscProfile = (value) => {
|
|
18983
|
-
if (!isRecord(value)) return null;
|
|
19255
|
+
if (!isRecord$1(value)) return null;
|
|
18984
19256
|
const { id, attributes } = value;
|
|
18985
|
-
if (typeof id !== "string" || !isRecord(attributes)) return null;
|
|
19257
|
+
if (typeof id !== "string" || !isRecord$1(attributes)) return null;
|
|
18986
19258
|
const { name, uuid, expirationDate, profileContent } = attributes;
|
|
18987
19259
|
const profileType = asProfileType(attributes["profileType"]);
|
|
18988
19260
|
if (typeof name !== "string" || typeof uuid !== "string" || typeof expirationDate !== "string" || typeof profileContent !== "string" || profileType === null) return null;
|
|
@@ -18996,9 +19268,9 @@ const toAscProfile = (value) => {
|
|
|
18996
19268
|
};
|
|
18997
19269
|
};
|
|
18998
19270
|
const toAscDevice = (value) => {
|
|
18999
|
-
if (!isRecord(value)) return null;
|
|
19271
|
+
if (!isRecord$1(value)) return null;
|
|
19000
19272
|
const { id, attributes } = value;
|
|
19001
|
-
if (typeof id !== "string" || !isRecord(attributes)) return null;
|
|
19273
|
+
if (typeof id !== "string" || !isRecord$1(attributes)) return null;
|
|
19002
19274
|
const { udid, name } = attributes;
|
|
19003
19275
|
if (typeof udid !== "string" || typeof name !== "string") return null;
|
|
19004
19276
|
return {
|
|
@@ -19008,11 +19280,11 @@ const toAscDevice = (value) => {
|
|
|
19008
19280
|
};
|
|
19009
19281
|
};
|
|
19010
19282
|
const extractList = (body, map) => {
|
|
19011
|
-
if (!isRecord(body) || !Array.isArray(body["data"])) return [];
|
|
19283
|
+
if (!isRecord$1(body) || !Array.isArray(body["data"])) return [];
|
|
19012
19284
|
return body["data"].map(map).filter((value) => value !== null);
|
|
19013
19285
|
};
|
|
19014
19286
|
const extractSingle = (body, map) => {
|
|
19015
|
-
if (!isRecord(body)) return null;
|
|
19287
|
+
if (!isRecord$1(body)) return null;
|
|
19016
19288
|
return map(body["data"]);
|
|
19017
19289
|
};
|
|
19018
19290
|
const malformed = (resource) => new AscApiError({
|
|
@@ -22647,7 +22919,7 @@ const runFingerprintFull = (projectRoot, options = {}) => Effect.gen(function* (
|
|
|
22647
22919
|
try: () => JSON.parse(stdout),
|
|
22648
22920
|
catch: () => new FingerprintError({ message: "Failed to parse @expo/fingerprint output as JSON." })
|
|
22649
22921
|
});
|
|
22650
|
-
if (!isRecord(parsed)) return yield* new FingerprintError({ message: "@expo/fingerprint output was not a JSON object." });
|
|
22922
|
+
if (!isRecord$1(parsed)) return yield* new FingerprintError({ message: "@expo/fingerprint output was not a JSON object." });
|
|
22651
22923
|
const { hash } = parsed;
|
|
22652
22924
|
if (typeof hash !== "string" || hash.length === 0) return yield* new FingerprintError({ message: "@expo/fingerprint output did not contain a \"hash\" string field." });
|
|
22653
22925
|
const sourcesRaw = parsed["sources"];
|
|
@@ -24264,7 +24536,7 @@ const compatibilityMatrixCommand = defineCommand({
|
|
|
24264
24536
|
|
|
24265
24537
|
//#endregion
|
|
24266
24538
|
//#region src/commands/builds/delete.ts
|
|
24267
|
-
const deleteCommand$
|
|
24539
|
+
const deleteCommand$5 = defineCommand({
|
|
24268
24540
|
meta: {
|
|
24269
24541
|
name: "delete",
|
|
24270
24542
|
description: "Delete a build"
|
|
@@ -24410,7 +24682,7 @@ const DISTRIBUTION_OPTIONS$1 = [
|
|
|
24410
24682
|
"play-store",
|
|
24411
24683
|
"direct"
|
|
24412
24684
|
];
|
|
24413
|
-
const listCommand$
|
|
24685
|
+
const listCommand$7 = defineCommand({
|
|
24414
24686
|
meta: {
|
|
24415
24687
|
name: "list",
|
|
24416
24688
|
description: "List builds for the linked project"
|
|
@@ -25068,9 +25340,9 @@ const buildsCommand = defineCommand({
|
|
|
25068
25340
|
description: "Manage builds"
|
|
25069
25341
|
},
|
|
25070
25342
|
subCommands: {
|
|
25071
|
-
list: listCommand$
|
|
25343
|
+
list: listCommand$7,
|
|
25072
25344
|
get: getCommand$2,
|
|
25073
|
-
delete: deleteCommand$
|
|
25345
|
+
delete: deleteCommand$5,
|
|
25074
25346
|
download: downloadCommand$1,
|
|
25075
25347
|
run: runCommand$1,
|
|
25076
25348
|
"install-link": installLinkCommand,
|
|
@@ -25096,7 +25368,7 @@ const resolveNamedResourceId$1 = (params) => resolveNamedResourceId$2(params, (m
|
|
|
25096
25368
|
|
|
25097
25369
|
//#endregion
|
|
25098
25370
|
//#region src/commands/channels/create.ts
|
|
25099
|
-
const createCommand$
|
|
25371
|
+
const createCommand$3 = defineCommand({
|
|
25100
25372
|
meta: {
|
|
25101
25373
|
name: "create",
|
|
25102
25374
|
description: "Create a channel"
|
|
@@ -25141,7 +25413,7 @@ const createCommand$4 = defineCommand({
|
|
|
25141
25413
|
|
|
25142
25414
|
//#endregion
|
|
25143
25415
|
//#region src/commands/channels/delete.ts
|
|
25144
|
-
const deleteCommand$
|
|
25416
|
+
const deleteCommand$4 = defineCommand({
|
|
25145
25417
|
meta: {
|
|
25146
25418
|
name: "delete",
|
|
25147
25419
|
description: "Delete a channel"
|
|
@@ -25164,193 +25436,6 @@ const deleteCommand$5 = defineCommand({
|
|
|
25164
25436
|
})
|
|
25165
25437
|
});
|
|
25166
25438
|
|
|
25167
|
-
//#endregion
|
|
25168
|
-
//#region src/commands/channels/grants/helpers.ts
|
|
25169
|
-
var GrantCommandError = class extends Data.TaggedError("GrantCommandError") {};
|
|
25170
|
-
const grantErrorExtras = { GrantCommandError: 2 };
|
|
25171
|
-
|
|
25172
|
-
//#endregion
|
|
25173
|
-
//#region src/commands/channels/grants/list.ts
|
|
25174
|
-
const resolveChannel = (channels, target) => Effect.gen(function* () {
|
|
25175
|
-
const channel = channels.find((ch) => ch.id === target) ?? channels.find((ch) => ch.name === target);
|
|
25176
|
-
if (!channel) return yield* new ChannelCommandError({ message: `Channel "${target}" not found by ID or name.` });
|
|
25177
|
-
return channel;
|
|
25178
|
-
});
|
|
25179
|
-
const listCommand$9 = defineCommand({
|
|
25180
|
-
meta: {
|
|
25181
|
-
name: "list",
|
|
25182
|
-
description: "List per-member grants on a channel"
|
|
25183
|
-
},
|
|
25184
|
-
args: { channel: {
|
|
25185
|
-
type: "positional",
|
|
25186
|
-
required: true,
|
|
25187
|
-
description: "Channel ID or channel name"
|
|
25188
|
-
} },
|
|
25189
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
25190
|
-
const projectId = yield* readProjectId;
|
|
25191
|
-
const api = yield* apiClient;
|
|
25192
|
-
const channel = yield* resolveChannel(yield* drainPages((page) => api.channels.list({ urlParams: {
|
|
25193
|
-
projectId,
|
|
25194
|
-
limit: 100,
|
|
25195
|
-
page
|
|
25196
|
-
} })), args.channel);
|
|
25197
|
-
yield* printList([
|
|
25198
|
-
"ID",
|
|
25199
|
-
"Member ID",
|
|
25200
|
-
"Effect",
|
|
25201
|
-
"Actions",
|
|
25202
|
-
"Created"
|
|
25203
|
-
], (yield* api.channelGrants.list({
|
|
25204
|
-
path: { id: channel.id },
|
|
25205
|
-
urlParams: {}
|
|
25206
|
-
})).map((grant) => [
|
|
25207
|
-
grant.id,
|
|
25208
|
-
grant.memberId,
|
|
25209
|
-
grant.effect,
|
|
25210
|
-
grant.actions.join(", "),
|
|
25211
|
-
grant.createdAt
|
|
25212
|
-
]), "No grants found for this channel.");
|
|
25213
|
-
}), { exits: {
|
|
25214
|
-
...channelErrorExtras,
|
|
25215
|
-
...grantErrorExtras
|
|
25216
|
-
} })
|
|
25217
|
-
});
|
|
25218
|
-
|
|
25219
|
-
//#endregion
|
|
25220
|
-
//#region src/commands/channels/grants/revoke.ts
|
|
25221
|
-
const revokeCommand$2 = defineCommand({
|
|
25222
|
-
meta: {
|
|
25223
|
-
name: "revoke",
|
|
25224
|
-
description: "Revoke all grants for a member on a channel"
|
|
25225
|
-
},
|
|
25226
|
-
args: {
|
|
25227
|
-
channel: {
|
|
25228
|
-
type: "positional",
|
|
25229
|
-
required: true,
|
|
25230
|
-
description: "Channel ID or channel name"
|
|
25231
|
-
},
|
|
25232
|
-
member: {
|
|
25233
|
-
type: "string",
|
|
25234
|
-
required: true,
|
|
25235
|
-
description: "Member ID whose grants to revoke"
|
|
25236
|
-
},
|
|
25237
|
-
yes: {
|
|
25238
|
-
type: "boolean",
|
|
25239
|
-
description: "Skip confirmation prompt"
|
|
25240
|
-
}
|
|
25241
|
-
},
|
|
25242
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
25243
|
-
if (!args.yes) {
|
|
25244
|
-
if (!(yield* promptConfirm(`Revoke all grants for member ${args.member} on channel ${args.channel}?`, { initialValue: false }))) {
|
|
25245
|
-
yield* printHuman("Cancelled.");
|
|
25246
|
-
return { deleted: 0 };
|
|
25247
|
-
}
|
|
25248
|
-
}
|
|
25249
|
-
const projectId = yield* readProjectId;
|
|
25250
|
-
const api = yield* apiClient;
|
|
25251
|
-
const channels = yield* drainPages((page) => api.channels.list({ urlParams: {
|
|
25252
|
-
projectId,
|
|
25253
|
-
limit: 100,
|
|
25254
|
-
page
|
|
25255
|
-
} }));
|
|
25256
|
-
const channel = channels.find((ch) => ch.id === args.channel) ?? channels.find((ch) => ch.name === args.channel);
|
|
25257
|
-
if (!channel) return yield* new ChannelCommandError({ message: `Channel "${args.channel}" not found by ID or name.` });
|
|
25258
|
-
const result = yield* api.channelGrants.delete({ path: {
|
|
25259
|
-
id: channel.id,
|
|
25260
|
-
memberId: args.member
|
|
25261
|
-
} });
|
|
25262
|
-
yield* printHuman(`Revoked grants for member ${args.member} on channel "${channel.name}".`);
|
|
25263
|
-
return result;
|
|
25264
|
-
}), {
|
|
25265
|
-
exits: {
|
|
25266
|
-
...channelErrorExtras,
|
|
25267
|
-
...grantErrorExtras
|
|
25268
|
-
},
|
|
25269
|
-
json: "value"
|
|
25270
|
-
})
|
|
25271
|
-
});
|
|
25272
|
-
|
|
25273
|
-
//#endregion
|
|
25274
|
-
//#region src/commands/channels/grants/set.ts
|
|
25275
|
-
const setCommand$3 = defineCommand({
|
|
25276
|
-
meta: {
|
|
25277
|
-
name: "set",
|
|
25278
|
-
description: "Create or replace a member's grant on a channel"
|
|
25279
|
-
},
|
|
25280
|
-
args: {
|
|
25281
|
-
channel: {
|
|
25282
|
-
type: "positional",
|
|
25283
|
-
required: true,
|
|
25284
|
-
description: "Channel ID or channel name"
|
|
25285
|
-
},
|
|
25286
|
-
member: {
|
|
25287
|
-
type: "string",
|
|
25288
|
-
required: true,
|
|
25289
|
-
description: "Member ID to grant permissions to"
|
|
25290
|
-
},
|
|
25291
|
-
actions: {
|
|
25292
|
-
type: "string",
|
|
25293
|
-
required: true,
|
|
25294
|
-
description: "Permission action tokens in resource:action format, comma-separated (e.g. update:create,rollout:update)"
|
|
25295
|
-
},
|
|
25296
|
-
effect: {
|
|
25297
|
-
type: "string",
|
|
25298
|
-
description: "Grant effect: allow (default) or deny"
|
|
25299
|
-
}
|
|
25300
|
-
},
|
|
25301
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
25302
|
-
const effectValue = args.effect ?? "allow";
|
|
25303
|
-
if (effectValue !== "allow" && effectValue !== "deny") return yield* new GrantCommandError({ message: `Invalid effect "${effectValue}" — must be "allow" or "deny".` });
|
|
25304
|
-
const actionTokens = args.actions.split(",").map((tok) => tok.trim()).filter((tok) => tok.length > 0);
|
|
25305
|
-
if (actionTokens.length === 0) return yield* new GrantCommandError({ message: "At least one action token is required." });
|
|
25306
|
-
const projectId = yield* readProjectId;
|
|
25307
|
-
const api = yield* apiClient;
|
|
25308
|
-
const channels = yield* drainPages((page) => api.channels.list({ urlParams: {
|
|
25309
|
-
projectId,
|
|
25310
|
-
limit: 100,
|
|
25311
|
-
page
|
|
25312
|
-
} }));
|
|
25313
|
-
const channel = channels.find((ch) => ch.id === args.channel) ?? channels.find((ch) => ch.name === args.channel);
|
|
25314
|
-
if (!channel) return yield* new ChannelCommandError({ message: `Channel "${args.channel}" not found by ID or name.` });
|
|
25315
|
-
const grant = yield* api.channelGrants.upsert({
|
|
25316
|
-
path: {
|
|
25317
|
-
id: channel.id,
|
|
25318
|
-
memberId: args.member
|
|
25319
|
-
},
|
|
25320
|
-
payload: {
|
|
25321
|
-
effect: effectValue,
|
|
25322
|
-
actions: actionTokens
|
|
25323
|
-
}
|
|
25324
|
-
});
|
|
25325
|
-
yield* printHumanKeyValue([
|
|
25326
|
-
["ID", grant.id],
|
|
25327
|
-
["Member ID", grant.memberId],
|
|
25328
|
-
["Channel ID", grant.scopeId],
|
|
25329
|
-
["Effect", grant.effect],
|
|
25330
|
-
["Actions", grant.actions.join(", ")],
|
|
25331
|
-
["Created", grant.createdAt]
|
|
25332
|
-
]);
|
|
25333
|
-
return grant;
|
|
25334
|
-
}), { exits: {
|
|
25335
|
-
...channelErrorExtras,
|
|
25336
|
-
...grantErrorExtras
|
|
25337
|
-
} })
|
|
25338
|
-
});
|
|
25339
|
-
|
|
25340
|
-
//#endregion
|
|
25341
|
-
//#region src/commands/channels/grants/index.ts
|
|
25342
|
-
const grantsCommand$1 = defineCommand({
|
|
25343
|
-
meta: {
|
|
25344
|
-
name: "grants",
|
|
25345
|
-
description: "Manage per-member permission grants on a channel"
|
|
25346
|
-
},
|
|
25347
|
-
subCommands: {
|
|
25348
|
-
list: listCommand$9,
|
|
25349
|
-
set: setCommand$3,
|
|
25350
|
-
revoke: revokeCommand$2
|
|
25351
|
-
}
|
|
25352
|
-
});
|
|
25353
|
-
|
|
25354
25439
|
//#endregion
|
|
25355
25440
|
//#region src/commands/channels/insights.ts
|
|
25356
25441
|
const insightsCommand$1 = defineCommand({
|
|
@@ -25397,7 +25482,7 @@ const insightsCommand$1 = defineCommand({
|
|
|
25397
25482
|
|
|
25398
25483
|
//#endregion
|
|
25399
25484
|
//#region src/commands/channels/list.ts
|
|
25400
|
-
const listCommand$
|
|
25485
|
+
const listCommand$6 = defineCommand({
|
|
25401
25486
|
meta: {
|
|
25402
25487
|
name: "list",
|
|
25403
25488
|
description: "List channels for the linked project"
|
|
@@ -25501,7 +25586,7 @@ const completeCommand$1 = defineCommand({
|
|
|
25501
25586
|
|
|
25502
25587
|
//#endregion
|
|
25503
25588
|
//#region src/commands/channels/rollout/create.ts
|
|
25504
|
-
const createCommand$
|
|
25589
|
+
const createCommand$2 = defineCommand({
|
|
25505
25590
|
meta: {
|
|
25506
25591
|
name: "create",
|
|
25507
25592
|
description: "Start a branch rollout on a channel"
|
|
@@ -25582,7 +25667,7 @@ const revertCommand$2 = defineCommand({
|
|
|
25582
25667
|
|
|
25583
25668
|
//#endregion
|
|
25584
25669
|
//#region src/commands/channels/rollout/update.ts
|
|
25585
|
-
const updateCommand$
|
|
25670
|
+
const updateCommand$3 = defineCommand({
|
|
25586
25671
|
meta: {
|
|
25587
25672
|
name: "update",
|
|
25588
25673
|
description: "Update the rollout percentage on a channel"
|
|
@@ -25621,8 +25706,8 @@ const rolloutCommand$1 = defineCommand({
|
|
|
25621
25706
|
description: "Manage channel branch rollouts"
|
|
25622
25707
|
},
|
|
25623
25708
|
subCommands: {
|
|
25624
|
-
create: createCommand$
|
|
25625
|
-
update: updateCommand$
|
|
25709
|
+
create: createCommand$2,
|
|
25710
|
+
update: updateCommand$3,
|
|
25626
25711
|
complete: completeCommand$1,
|
|
25627
25712
|
revert: revertCommand$2
|
|
25628
25713
|
}
|
|
@@ -25630,7 +25715,7 @@ const rolloutCommand$1 = defineCommand({
|
|
|
25630
25715
|
|
|
25631
25716
|
//#endregion
|
|
25632
25717
|
//#region src/commands/channels/update.ts
|
|
25633
|
-
const updateCommand$
|
|
25718
|
+
const updateCommand$2 = defineCommand({
|
|
25634
25719
|
meta: {
|
|
25635
25720
|
name: "update",
|
|
25636
25721
|
description: "Relink a channel to a different branch"
|
|
@@ -25673,7 +25758,7 @@ const updateCommand$3 = defineCommand({
|
|
|
25673
25758
|
|
|
25674
25759
|
//#endregion
|
|
25675
25760
|
//#region src/commands/channels/view.ts
|
|
25676
|
-
const viewCommand$
|
|
25761
|
+
const viewCommand$2 = defineCommand({
|
|
25677
25762
|
meta: {
|
|
25678
25763
|
name: "view",
|
|
25679
25764
|
description: "Show a channel by ID or name"
|
|
@@ -25733,15 +25818,14 @@ const channelsCommand = defineCommand({
|
|
|
25733
25818
|
description: "Manage channels"
|
|
25734
25819
|
},
|
|
25735
25820
|
subCommands: {
|
|
25736
|
-
list: listCommand$
|
|
25737
|
-
view: viewCommand$
|
|
25738
|
-
create: createCommand$
|
|
25739
|
-
update: updateCommand$
|
|
25821
|
+
list: listCommand$6,
|
|
25822
|
+
view: viewCommand$2,
|
|
25823
|
+
create: createCommand$3,
|
|
25824
|
+
update: updateCommand$2,
|
|
25740
25825
|
pause: pauseCommand,
|
|
25741
25826
|
resume: resumeCommand,
|
|
25742
|
-
delete: deleteCommand$
|
|
25827
|
+
delete: deleteCommand$4,
|
|
25743
25828
|
rollout: rolloutCommand$1,
|
|
25744
|
-
grants: grantsCommand$1,
|
|
25745
25829
|
insights: insightsCommand$1
|
|
25746
25830
|
}
|
|
25747
25831
|
});
|
|
@@ -27155,7 +27239,7 @@ const toRecipientView = (userEncryptionKeyId, key) => ({
|
|
|
27155
27239
|
fingerprint: key?.fingerprint
|
|
27156
27240
|
})
|
|
27157
27241
|
});
|
|
27158
|
-
const listCommand$
|
|
27242
|
+
const listCommand$5 = defineCommand({
|
|
27159
27243
|
meta: {
|
|
27160
27244
|
name: "list",
|
|
27161
27245
|
description: "List recipients that currently hold the org vault key"
|
|
@@ -27382,7 +27466,7 @@ const accessCommand = defineCommand({
|
|
|
27382
27466
|
description: "Inspect, grant, rotate, revoke, and recover access to the org credential vault"
|
|
27383
27467
|
},
|
|
27384
27468
|
subCommands: {
|
|
27385
|
-
list: listCommand$
|
|
27469
|
+
list: listCommand$5,
|
|
27386
27470
|
grant: grantCommand,
|
|
27387
27471
|
rotate: rotateCommand,
|
|
27388
27472
|
revoke: revokeCommand$1,
|
|
@@ -27591,7 +27675,7 @@ const CREDENTIAL_TYPES$3 = [
|
|
|
27591
27675
|
"keystore",
|
|
27592
27676
|
"google-service-account-key"
|
|
27593
27677
|
];
|
|
27594
|
-
const deleteCommand$
|
|
27678
|
+
const deleteCommand$3 = defineCommand({
|
|
27595
27679
|
meta: {
|
|
27596
27680
|
name: "delete",
|
|
27597
27681
|
description: "Delete a credential"
|
|
@@ -27633,7 +27717,7 @@ const deleteCommand$4 = defineCommand({
|
|
|
27633
27717
|
//#region src/commands/credentials/device.ts
|
|
27634
27718
|
/** Self-linking is for your own device keys; recovery/machine keys go through `access grant`. */
|
|
27635
27719
|
const requireDeviceKind = (target) => target.kind === "device" ? Effect.void : new IdentityError({ message: `Key ${target.id} is a ${target.kind} key, not a device. Use \`better-update credentials access grant\` for recovery/machine keys.` });
|
|
27636
|
-
const listCommand$
|
|
27720
|
+
const listCommand$4 = defineCommand({
|
|
27637
27721
|
meta: {
|
|
27638
27722
|
name: "list",
|
|
27639
27723
|
description: "List your registered device keys (the active one is marked)"
|
|
@@ -27691,7 +27775,7 @@ const deviceCommand = defineCommand({
|
|
|
27691
27775
|
description: "Manage your vault device keys"
|
|
27692
27776
|
},
|
|
27693
27777
|
subCommands: {
|
|
27694
|
-
list: listCommand$
|
|
27778
|
+
list: listCommand$4,
|
|
27695
27779
|
link: linkCommand
|
|
27696
27780
|
},
|
|
27697
27781
|
default: "list"
|
|
@@ -28346,7 +28430,7 @@ const printRecipient = (key) => printKeyValue([
|
|
|
28346
28430
|
["Recipient (public key)", key.publicKey],
|
|
28347
28431
|
["Fingerprint", key.fingerprint]
|
|
28348
28432
|
]);
|
|
28349
|
-
const createCommand$
|
|
28433
|
+
const createCommand$1 = defineCommand({
|
|
28350
28434
|
meta: {
|
|
28351
28435
|
name: "create",
|
|
28352
28436
|
description: "Create this device's encryption identity and register it as a recipient"
|
|
@@ -28449,7 +28533,7 @@ const identityCommand = defineCommand({
|
|
|
28449
28533
|
description: "Manage this device's end-to-end encryption identity"
|
|
28450
28534
|
},
|
|
28451
28535
|
subCommands: {
|
|
28452
|
-
create: createCommand$
|
|
28536
|
+
create: createCommand$1,
|
|
28453
28537
|
init: initCommand$1,
|
|
28454
28538
|
register: registerCommand,
|
|
28455
28539
|
show: showCommand
|
|
@@ -28459,7 +28543,7 @@ const identityCommand = defineCommand({
|
|
|
28459
28543
|
|
|
28460
28544
|
//#endregion
|
|
28461
28545
|
//#region src/commands/credentials/list.ts
|
|
28462
|
-
const listCommand$
|
|
28546
|
+
const listCommand$3 = defineCommand({
|
|
28463
28547
|
meta: {
|
|
28464
28548
|
name: "list",
|
|
28465
28549
|
description: "List credentials across platforms"
|
|
@@ -29614,7 +29698,7 @@ const lookupByType = (api, id, type) => {
|
|
|
29614
29698
|
default: return Effect.fail(new CredentialValidationError({ message: `Unsupported credential type: ${String(type)}` }));
|
|
29615
29699
|
}
|
|
29616
29700
|
};
|
|
29617
|
-
const viewCommand$
|
|
29701
|
+
const viewCommand$1 = defineCommand({
|
|
29618
29702
|
meta: {
|
|
29619
29703
|
name: "view",
|
|
29620
29704
|
description: "Show details for a single credential (without secrets)"
|
|
@@ -29661,14 +29745,14 @@ const credentialsCommand = defineCommand({
|
|
|
29661
29745
|
unlock: unlockCommand,
|
|
29662
29746
|
lock: lockCommand,
|
|
29663
29747
|
status: statusCommand$1,
|
|
29664
|
-
list: listCommand$
|
|
29665
|
-
view: viewCommand$
|
|
29748
|
+
list: listCommand$3,
|
|
29749
|
+
view: viewCommand$1,
|
|
29666
29750
|
download: downloadCommand,
|
|
29667
29751
|
upload: uploadCommand,
|
|
29668
29752
|
"upload-asc-key": uploadAscKeyCommand,
|
|
29669
29753
|
generate: generateCommand$1,
|
|
29670
29754
|
"regenerate-profile": regenerateProfileCommand,
|
|
29671
|
-
delete: deleteCommand$
|
|
29755
|
+
delete: deleteCommand$3,
|
|
29672
29756
|
remove: removeCommand,
|
|
29673
29757
|
revoke: revokeCommand,
|
|
29674
29758
|
configure: configureCommand$1,
|
|
@@ -30168,19 +30252,19 @@ const envErrorExtras = {
|
|
|
30168
30252
|
SystemError: 6,
|
|
30169
30253
|
BadArgument: 6
|
|
30170
30254
|
};
|
|
30171
|
-
const isEnvironmentName
|
|
30255
|
+
const isEnvironmentName = (value) => value === "development" || value === "preview" || value === "production";
|
|
30172
30256
|
const parseEnvironmentsArg = (raw) => Effect.gen(function* () {
|
|
30173
30257
|
const tokens = raw.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
30174
30258
|
if (tokens.length === 0) return yield* new InvalidArgumentError({ message: "Provide at least one environment (development, preview, production)." });
|
|
30175
30259
|
const seen = /* @__PURE__ */ new Set();
|
|
30176
30260
|
yield* Effect.forEach(tokens, (token) => Effect.gen(function* () {
|
|
30177
|
-
if (!isEnvironmentName
|
|
30261
|
+
if (!isEnvironmentName(token)) return yield* new InvalidArgumentError({ message: `Invalid environment "${token}". Must be one of: development, preview, production.` });
|
|
30178
30262
|
seen.add(token);
|
|
30179
30263
|
}), { discard: true });
|
|
30180
30264
|
return [...seen];
|
|
30181
30265
|
});
|
|
30182
30266
|
const parseSingleEnvironmentArg = (raw) => Effect.gen(function* () {
|
|
30183
|
-
if (!isEnvironmentName
|
|
30267
|
+
if (!isEnvironmentName(raw)) return yield* new InvalidArgumentError({ message: `Invalid environment "${raw}". Must be one of: development, preview, production.` });
|
|
30184
30268
|
return raw;
|
|
30185
30269
|
});
|
|
30186
30270
|
const formatEnvironments = (environments) => [...environments].toSorted((left, right) => left.localeCompare(right)).join(",");
|
|
@@ -30219,7 +30303,7 @@ const findProjectEnvVar = (api, projectId, key, environment) => Effect.gen(funct
|
|
|
30219
30303
|
|
|
30220
30304
|
//#endregion
|
|
30221
30305
|
//#region src/commands/env/delete.ts
|
|
30222
|
-
const deleteCommand$
|
|
30306
|
+
const deleteCommand$2 = defineCommand({
|
|
30223
30307
|
meta: {
|
|
30224
30308
|
name: "delete",
|
|
30225
30309
|
description: "Delete a project env var (one environment, or every environment by default)"
|
|
@@ -30391,191 +30475,6 @@ const getCommand$1 = defineCommand({
|
|
|
30391
30475
|
}), envErrorExtras)
|
|
30392
30476
|
});
|
|
30393
30477
|
|
|
30394
|
-
//#endregion
|
|
30395
|
-
//#region src/commands/env/grants/helpers.ts
|
|
30396
|
-
var EnvGrantCommandError = class extends Data.TaggedError("EnvGrantCommandError") {};
|
|
30397
|
-
const envGrantErrorExtras = { EnvGrantCommandError: 2 };
|
|
30398
|
-
/** Sentinel project token for the org-global env-var scope (mirrors server). */
|
|
30399
|
-
const ENV_GRANT_GLOBAL = "global";
|
|
30400
|
-
const ENVIRONMENTS = [
|
|
30401
|
-
"development",
|
|
30402
|
-
"preview",
|
|
30403
|
-
"production"
|
|
30404
|
-
];
|
|
30405
|
-
/**
|
|
30406
|
-
* Type guard narrowing a raw arg to an {@link EnvironmentName}. Lets set/unset
|
|
30407
|
-
* validate `args.environment` AND narrow it without an unsafe `as` assertion.
|
|
30408
|
-
*/
|
|
30409
|
-
const isEnvironmentName = (value) => ENVIRONMENTS.includes(value);
|
|
30410
|
-
|
|
30411
|
-
//#endregion
|
|
30412
|
-
//#region src/commands/env/grants/list.ts
|
|
30413
|
-
const listCommand$4 = defineCommand({
|
|
30414
|
-
meta: {
|
|
30415
|
-
name: "list",
|
|
30416
|
-
description: "List env-var grants on a (project × environment) scope"
|
|
30417
|
-
},
|
|
30418
|
-
args: {
|
|
30419
|
-
project: {
|
|
30420
|
-
type: "string",
|
|
30421
|
-
description: `Project id, or "${ENV_GRANT_GLOBAL}" for the org-global scope (default: linked project)`
|
|
30422
|
-
},
|
|
30423
|
-
global: {
|
|
30424
|
-
type: "boolean",
|
|
30425
|
-
default: false,
|
|
30426
|
-
description: "Target the org-global env-var scope instead of a project"
|
|
30427
|
-
}
|
|
30428
|
-
},
|
|
30429
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
30430
|
-
const projectId = args.global ? ENV_GRANT_GLOBAL : args.project ?? (yield* readProjectId);
|
|
30431
|
-
yield* printList([
|
|
30432
|
-
"Member ID",
|
|
30433
|
-
"Environment",
|
|
30434
|
-
"Effect",
|
|
30435
|
-
"Actions"
|
|
30436
|
-
], (yield* (yield* apiClient).envGrants.list({ urlParams: { projectId } })).map((row) => [
|
|
30437
|
-
row.memberId,
|
|
30438
|
-
row.environment,
|
|
30439
|
-
row.effect,
|
|
30440
|
-
row.actions.join(", ")
|
|
30441
|
-
]), "No env-var grants found for this scope.");
|
|
30442
|
-
}), { exits: { ...envGrantErrorExtras } })
|
|
30443
|
-
});
|
|
30444
|
-
|
|
30445
|
-
//#endregion
|
|
30446
|
-
//#region src/commands/env/grants/set.ts
|
|
30447
|
-
const setCommand$2 = defineCommand({
|
|
30448
|
-
meta: {
|
|
30449
|
-
name: "set",
|
|
30450
|
-
description: "Create or replace a member's env-var grant on a scope"
|
|
30451
|
-
},
|
|
30452
|
-
args: {
|
|
30453
|
-
member: {
|
|
30454
|
-
type: "string",
|
|
30455
|
-
required: true,
|
|
30456
|
-
description: "Member ID to grant"
|
|
30457
|
-
},
|
|
30458
|
-
environment: {
|
|
30459
|
-
type: "string",
|
|
30460
|
-
required: true,
|
|
30461
|
-
description: "Environment: development | preview | production"
|
|
30462
|
-
},
|
|
30463
|
-
actions: {
|
|
30464
|
-
type: "string",
|
|
30465
|
-
description: "Comma-separated envVar:* tokens (default: envVar:read)"
|
|
30466
|
-
},
|
|
30467
|
-
effect: {
|
|
30468
|
-
type: "string",
|
|
30469
|
-
description: "allow (default) or deny"
|
|
30470
|
-
},
|
|
30471
|
-
project: {
|
|
30472
|
-
type: "string",
|
|
30473
|
-
description: `Project id (default: linked project)`
|
|
30474
|
-
},
|
|
30475
|
-
global: {
|
|
30476
|
-
type: "boolean",
|
|
30477
|
-
default: false,
|
|
30478
|
-
description: "Target the org-global env-var scope"
|
|
30479
|
-
}
|
|
30480
|
-
},
|
|
30481
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
30482
|
-
const effectValue = args.effect ?? "allow";
|
|
30483
|
-
if (effectValue !== "allow" && effectValue !== "deny") return yield* new EnvGrantCommandError({ message: `Invalid effect "${effectValue}".` });
|
|
30484
|
-
const { environment } = args;
|
|
30485
|
-
if (!isEnvironmentName(environment)) return yield* new EnvGrantCommandError({ message: `Invalid environment "${environment}". One of: ${ENVIRONMENTS.join(", ")}.` });
|
|
30486
|
-
const actionTokens = (args.actions ?? "envVar:read").split(",").map((tok) => tok.trim()).filter((tok) => tok.length > 0);
|
|
30487
|
-
if (actionTokens.length === 0) return yield* new EnvGrantCommandError({ message: "At least one action token is required." });
|
|
30488
|
-
const projectId = args.global ? null : args.project ?? (yield* readProjectId);
|
|
30489
|
-
const grant = yield* (yield* apiClient).envGrants.upsert({ payload: {
|
|
30490
|
-
memberId: args.member,
|
|
30491
|
-
projectId,
|
|
30492
|
-
environment,
|
|
30493
|
-
effect: effectValue,
|
|
30494
|
-
actions: actionTokens
|
|
30495
|
-
} });
|
|
30496
|
-
yield* printHumanKeyValue([
|
|
30497
|
-
["ID", grant.id],
|
|
30498
|
-
["Member ID", grant.memberId],
|
|
30499
|
-
["Scope", grant.scopeId],
|
|
30500
|
-
["Effect", grant.effect],
|
|
30501
|
-
["Actions", grant.actions.join(", ")],
|
|
30502
|
-
["Created", grant.createdAt]
|
|
30503
|
-
]);
|
|
30504
|
-
return grant;
|
|
30505
|
-
}), { exits: { ...envGrantErrorExtras } })
|
|
30506
|
-
});
|
|
30507
|
-
|
|
30508
|
-
//#endregion
|
|
30509
|
-
//#region src/commands/env/grants/unset.ts
|
|
30510
|
-
const unsetCommand = defineCommand({
|
|
30511
|
-
meta: {
|
|
30512
|
-
name: "unset",
|
|
30513
|
-
description: "Revoke a member's env-var grants on a scope"
|
|
30514
|
-
},
|
|
30515
|
-
args: {
|
|
30516
|
-
member: {
|
|
30517
|
-
type: "string",
|
|
30518
|
-
required: true,
|
|
30519
|
-
description: "Member ID whose grants to revoke"
|
|
30520
|
-
},
|
|
30521
|
-
environment: {
|
|
30522
|
-
type: "string",
|
|
30523
|
-
required: true,
|
|
30524
|
-
description: "Environment"
|
|
30525
|
-
},
|
|
30526
|
-
project: {
|
|
30527
|
-
type: "string",
|
|
30528
|
-
description: "Project id (default: linked project)"
|
|
30529
|
-
},
|
|
30530
|
-
global: {
|
|
30531
|
-
type: "boolean",
|
|
30532
|
-
default: false,
|
|
30533
|
-
description: "Target the org-global scope"
|
|
30534
|
-
},
|
|
30535
|
-
yes: {
|
|
30536
|
-
type: "boolean",
|
|
30537
|
-
default: false,
|
|
30538
|
-
description: "Skip confirmation prompt"
|
|
30539
|
-
}
|
|
30540
|
-
},
|
|
30541
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
30542
|
-
const { environment } = args;
|
|
30543
|
-
if (!isEnvironmentName(environment)) return yield* new EnvGrantCommandError({ message: `Invalid environment "${environment}".` });
|
|
30544
|
-
if (!args.yes) {
|
|
30545
|
-
const scopeLabel = args.global ? ENV_GRANT_GLOBAL : args.project ?? "linked project";
|
|
30546
|
-
if (!(yield* promptConfirm(`Revoke env-var grants for member ${args.member} on ${scopeLabel}/${args.environment}?`, { initialValue: false }))) {
|
|
30547
|
-
yield* printHuman("Cancelled.");
|
|
30548
|
-
return { deleted: 0 };
|
|
30549
|
-
}
|
|
30550
|
-
}
|
|
30551
|
-
const projectId = args.global ? null : args.project ?? (yield* readProjectId);
|
|
30552
|
-
const result = yield* (yield* apiClient).envGrants.delete({ payload: {
|
|
30553
|
-
memberId: args.member,
|
|
30554
|
-
projectId,
|
|
30555
|
-
environment
|
|
30556
|
-
} });
|
|
30557
|
-
yield* printHuman(`Revoked env-var grants for member ${args.member}.`);
|
|
30558
|
-
return result;
|
|
30559
|
-
}), {
|
|
30560
|
-
exits: { ...envGrantErrorExtras },
|
|
30561
|
-
json: "value"
|
|
30562
|
-
})
|
|
30563
|
-
});
|
|
30564
|
-
|
|
30565
|
-
//#endregion
|
|
30566
|
-
//#region src/commands/env/grants/index.ts
|
|
30567
|
-
const grantsCommand = defineCommand({
|
|
30568
|
-
meta: {
|
|
30569
|
-
name: "grants",
|
|
30570
|
-
description: "Manage per-member env-var access grants on a (project × environment) scope"
|
|
30571
|
-
},
|
|
30572
|
-
subCommands: {
|
|
30573
|
-
list: listCommand$4,
|
|
30574
|
-
set: setCommand$2,
|
|
30575
|
-
unset: unsetCommand
|
|
30576
|
-
}
|
|
30577
|
-
});
|
|
30578
|
-
|
|
30579
30478
|
//#endregion
|
|
30580
30479
|
//#region src/commands/env/history.ts
|
|
30581
30480
|
const historyCommand = defineCommand({
|
|
@@ -30674,7 +30573,7 @@ const importCommand = defineCommand({
|
|
|
30674
30573
|
|
|
30675
30574
|
//#endregion
|
|
30676
30575
|
//#region src/commands/env/list.ts
|
|
30677
|
-
const listCommand$
|
|
30576
|
+
const listCommand$2 = defineCommand({
|
|
30678
30577
|
meta: {
|
|
30679
30578
|
name: "list",
|
|
30680
30579
|
description: "List environment variable metadata. Values are end-to-end encrypted — read them with `env pull`, `env export`, or `env get`."
|
|
@@ -30940,7 +30839,7 @@ const setCommand$1 = defineCommand({
|
|
|
30940
30839
|
|
|
30941
30840
|
//#endregion
|
|
30942
30841
|
//#region src/commands/env/update.ts
|
|
30943
|
-
const updateCommand$
|
|
30842
|
+
const updateCommand$1 = defineCommand({
|
|
30944
30843
|
meta: {
|
|
30945
30844
|
name: "update",
|
|
30946
30845
|
description: "Update a project env var's value or visibility for an environment"
|
|
@@ -31021,19 +30920,18 @@ const envCommand = defineCommand({
|
|
|
31021
30920
|
description: "Manage environment variables"
|
|
31022
30921
|
},
|
|
31023
30922
|
subCommands: {
|
|
31024
|
-
list: listCommand$
|
|
30923
|
+
list: listCommand$2,
|
|
31025
30924
|
get: getCommand$1,
|
|
31026
30925
|
set: setCommand$1,
|
|
31027
|
-
update: updateCommand$
|
|
31028
|
-
delete: deleteCommand$
|
|
30926
|
+
update: updateCommand$1,
|
|
30927
|
+
delete: deleteCommand$2,
|
|
31029
30928
|
history: historyCommand,
|
|
31030
30929
|
rollback: rollbackCommand$1,
|
|
31031
30930
|
import: importCommand,
|
|
31032
30931
|
push: pushCommand,
|
|
31033
30932
|
export: exportCommand,
|
|
31034
30933
|
pull: pullCommand,
|
|
31035
|
-
exec: execCommand
|
|
31036
|
-
grants: grantsCommand
|
|
30934
|
+
exec: execCommand
|
|
31037
30935
|
}
|
|
31038
30936
|
});
|
|
31039
30937
|
|
|
@@ -31299,6 +31197,376 @@ const fingerprintCommand = defineCommand({
|
|
|
31299
31197
|
}
|
|
31300
31198
|
});
|
|
31301
31199
|
|
|
31200
|
+
//#endregion
|
|
31201
|
+
//#region src/commands/groups/helpers.ts
|
|
31202
|
+
const groupErrorExtras = { GroupCommandError: 2 };
|
|
31203
|
+
|
|
31204
|
+
//#endregion
|
|
31205
|
+
//#region src/commands/groups/attach.ts
|
|
31206
|
+
const attachGroupPolicyCommand = defineCommand({
|
|
31207
|
+
meta: {
|
|
31208
|
+
name: "attach",
|
|
31209
|
+
description: "Attach a policy (real or managed:*) to a group; members inherit it"
|
|
31210
|
+
},
|
|
31211
|
+
args: {
|
|
31212
|
+
id: {
|
|
31213
|
+
type: "positional",
|
|
31214
|
+
required: true,
|
|
31215
|
+
description: "Group ID"
|
|
31216
|
+
},
|
|
31217
|
+
"policy-id": {
|
|
31218
|
+
type: "string",
|
|
31219
|
+
required: true,
|
|
31220
|
+
description: "Policy ID to attach (real id or managed preset like managed:admin)"
|
|
31221
|
+
}
|
|
31222
|
+
},
|
|
31223
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31224
|
+
const attachment = yield* (yield* apiClient)["policy-attachments"].attachToGroup({
|
|
31225
|
+
path: { id: args.id },
|
|
31226
|
+
payload: { policyId: args["policy-id"] }
|
|
31227
|
+
});
|
|
31228
|
+
yield* printHumanKeyValue([
|
|
31229
|
+
["Attachment ID", attachment.id],
|
|
31230
|
+
["Group ID", attachment.principalId],
|
|
31231
|
+
["Policy ID", attachment.policyId],
|
|
31232
|
+
["Created", attachment.createdAt]
|
|
31233
|
+
]);
|
|
31234
|
+
return attachment;
|
|
31235
|
+
}), {
|
|
31236
|
+
exits: groupErrorExtras,
|
|
31237
|
+
json: "value"
|
|
31238
|
+
})
|
|
31239
|
+
});
|
|
31240
|
+
|
|
31241
|
+
//#endregion
|
|
31242
|
+
//#region src/commands/groups/create.ts
|
|
31243
|
+
const createGroupCommand = defineCommand({
|
|
31244
|
+
meta: {
|
|
31245
|
+
name: "create",
|
|
31246
|
+
description: "Create a member group"
|
|
31247
|
+
},
|
|
31248
|
+
args: {
|
|
31249
|
+
name: {
|
|
31250
|
+
type: "string",
|
|
31251
|
+
required: true,
|
|
31252
|
+
description: "Group display name"
|
|
31253
|
+
},
|
|
31254
|
+
description: {
|
|
31255
|
+
type: "string",
|
|
31256
|
+
description: "Optional human description"
|
|
31257
|
+
}
|
|
31258
|
+
},
|
|
31259
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31260
|
+
const group = yield* (yield* apiClient).groups.create({ payload: {
|
|
31261
|
+
name: args.name,
|
|
31262
|
+
...compact({ description: args.description })
|
|
31263
|
+
} });
|
|
31264
|
+
yield* printHumanKeyValue([
|
|
31265
|
+
["ID", group.id],
|
|
31266
|
+
["Name", group.name],
|
|
31267
|
+
["Description", group.description ?? "-"],
|
|
31268
|
+
["Created", group.createdAt]
|
|
31269
|
+
]);
|
|
31270
|
+
return group;
|
|
31271
|
+
}), {
|
|
31272
|
+
exits: groupErrorExtras,
|
|
31273
|
+
json: "value"
|
|
31274
|
+
})
|
|
31275
|
+
});
|
|
31276
|
+
|
|
31277
|
+
//#endregion
|
|
31278
|
+
//#region src/commands/groups/delete.ts
|
|
31279
|
+
const deleteGroupCommand = defineCommand({
|
|
31280
|
+
meta: {
|
|
31281
|
+
name: "delete",
|
|
31282
|
+
description: "Delete a group and sweep its memberships and policy attachments"
|
|
31283
|
+
},
|
|
31284
|
+
args: {
|
|
31285
|
+
id: {
|
|
31286
|
+
type: "positional",
|
|
31287
|
+
required: true,
|
|
31288
|
+
description: "Group ID"
|
|
31289
|
+
},
|
|
31290
|
+
yes: {
|
|
31291
|
+
type: "boolean",
|
|
31292
|
+
description: "Skip confirmation prompt"
|
|
31293
|
+
}
|
|
31294
|
+
},
|
|
31295
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31296
|
+
if (!args.yes) {
|
|
31297
|
+
if (!(yield* promptConfirm(`Delete group ${args.id}?`, { initialValue: false }))) {
|
|
31298
|
+
yield* printHuman("Cancelled.");
|
|
31299
|
+
return;
|
|
31300
|
+
}
|
|
31301
|
+
}
|
|
31302
|
+
yield* (yield* apiClient).groups.delete({ path: { id: args.id } });
|
|
31303
|
+
yield* printHuman(`Deleted group ${args.id}.`);
|
|
31304
|
+
return {
|
|
31305
|
+
id: args.id,
|
|
31306
|
+
deleted: true
|
|
31307
|
+
};
|
|
31308
|
+
}), {
|
|
31309
|
+
exits: groupErrorExtras,
|
|
31310
|
+
json: "value"
|
|
31311
|
+
})
|
|
31312
|
+
});
|
|
31313
|
+
|
|
31314
|
+
//#endregion
|
|
31315
|
+
//#region src/commands/groups/detach.ts
|
|
31316
|
+
const detachGroupPolicyCommand = defineCommand({
|
|
31317
|
+
meta: {
|
|
31318
|
+
name: "detach",
|
|
31319
|
+
description: "Remove a policy attachment from a group"
|
|
31320
|
+
},
|
|
31321
|
+
args: {
|
|
31322
|
+
id: {
|
|
31323
|
+
type: "positional",
|
|
31324
|
+
required: true,
|
|
31325
|
+
description: "Group ID"
|
|
31326
|
+
},
|
|
31327
|
+
"policy-id": {
|
|
31328
|
+
type: "string",
|
|
31329
|
+
required: true,
|
|
31330
|
+
description: "Policy ID to detach (real id or managed preset like managed:admin)"
|
|
31331
|
+
}
|
|
31332
|
+
},
|
|
31333
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31334
|
+
yield* (yield* apiClient)["policy-attachments"].detachFromGroup({ path: {
|
|
31335
|
+
id: args.id,
|
|
31336
|
+
policyId: encodeURIComponent(args["policy-id"])
|
|
31337
|
+
} });
|
|
31338
|
+
yield* printHuman(`Detached policy ${args["policy-id"]} from group ${args.id}.`);
|
|
31339
|
+
return {
|
|
31340
|
+
groupId: args.id,
|
|
31341
|
+
policyId: args["policy-id"],
|
|
31342
|
+
detached: true
|
|
31343
|
+
};
|
|
31344
|
+
}), {
|
|
31345
|
+
exits: groupErrorExtras,
|
|
31346
|
+
json: "value"
|
|
31347
|
+
})
|
|
31348
|
+
});
|
|
31349
|
+
|
|
31350
|
+
//#endregion
|
|
31351
|
+
//#region src/commands/groups/list.ts
|
|
31352
|
+
const listGroupsCommand = defineCommand({
|
|
31353
|
+
meta: {
|
|
31354
|
+
name: "list",
|
|
31355
|
+
description: "List member groups in the active organization"
|
|
31356
|
+
},
|
|
31357
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
31358
|
+
const result = yield* (yield* apiClient).groups.list();
|
|
31359
|
+
yield* printHumanTable([
|
|
31360
|
+
"ID",
|
|
31361
|
+
"Name",
|
|
31362
|
+
"Description",
|
|
31363
|
+
"Created"
|
|
31364
|
+
], result.items.map((group) => [
|
|
31365
|
+
group.id,
|
|
31366
|
+
group.name,
|
|
31367
|
+
group.description ?? "-",
|
|
31368
|
+
group.createdAt
|
|
31369
|
+
]));
|
|
31370
|
+
return result;
|
|
31371
|
+
}), {
|
|
31372
|
+
exits: groupErrorExtras,
|
|
31373
|
+
json: "value"
|
|
31374
|
+
})
|
|
31375
|
+
});
|
|
31376
|
+
|
|
31377
|
+
//#endregion
|
|
31378
|
+
//#region src/commands/groups/members/add.ts
|
|
31379
|
+
const addGroupMemberCommand = defineCommand({
|
|
31380
|
+
meta: {
|
|
31381
|
+
name: "add",
|
|
31382
|
+
description: "Add an organization member to a group"
|
|
31383
|
+
},
|
|
31384
|
+
args: {
|
|
31385
|
+
id: {
|
|
31386
|
+
type: "positional",
|
|
31387
|
+
required: true,
|
|
31388
|
+
description: "Group ID"
|
|
31389
|
+
},
|
|
31390
|
+
"member-id": {
|
|
31391
|
+
type: "string",
|
|
31392
|
+
required: true,
|
|
31393
|
+
description: "Organization member ID to add"
|
|
31394
|
+
}
|
|
31395
|
+
},
|
|
31396
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31397
|
+
const member = yield* (yield* apiClient).groups.addMember({
|
|
31398
|
+
path: { id: args.id },
|
|
31399
|
+
payload: { memberId: args["member-id"] }
|
|
31400
|
+
});
|
|
31401
|
+
yield* printHumanKeyValue([["Member ID", member.memberId], ["Added", member.createdAt]]);
|
|
31402
|
+
return member;
|
|
31403
|
+
}), {
|
|
31404
|
+
exits: groupErrorExtras,
|
|
31405
|
+
json: "value"
|
|
31406
|
+
})
|
|
31407
|
+
});
|
|
31408
|
+
|
|
31409
|
+
//#endregion
|
|
31410
|
+
//#region src/commands/groups/members/remove.ts
|
|
31411
|
+
const removeGroupMemberCommand = defineCommand({
|
|
31412
|
+
meta: {
|
|
31413
|
+
name: "remove",
|
|
31414
|
+
description: "Remove a member from a group"
|
|
31415
|
+
},
|
|
31416
|
+
args: {
|
|
31417
|
+
id: {
|
|
31418
|
+
type: "positional",
|
|
31419
|
+
required: true,
|
|
31420
|
+
description: "Group ID"
|
|
31421
|
+
},
|
|
31422
|
+
"member-id": {
|
|
31423
|
+
type: "string",
|
|
31424
|
+
required: true,
|
|
31425
|
+
description: "Organization member ID to remove"
|
|
31426
|
+
}
|
|
31427
|
+
},
|
|
31428
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31429
|
+
yield* (yield* apiClient).groups.removeMember({ path: {
|
|
31430
|
+
id: args.id,
|
|
31431
|
+
memberId: args["member-id"]
|
|
31432
|
+
} });
|
|
31433
|
+
yield* printHuman(`Removed member ${args["member-id"]} from group ${args.id}.`);
|
|
31434
|
+
return {
|
|
31435
|
+
groupId: args.id,
|
|
31436
|
+
memberId: args["member-id"],
|
|
31437
|
+
removed: true
|
|
31438
|
+
};
|
|
31439
|
+
}), {
|
|
31440
|
+
exits: groupErrorExtras,
|
|
31441
|
+
json: "value"
|
|
31442
|
+
})
|
|
31443
|
+
});
|
|
31444
|
+
|
|
31445
|
+
//#endregion
|
|
31446
|
+
//#region src/commands/groups/members/index.ts
|
|
31447
|
+
const listGroupMembersCommand = defineCommand({
|
|
31448
|
+
meta: {
|
|
31449
|
+
name: "list",
|
|
31450
|
+
description: "List the members belonging to a group"
|
|
31451
|
+
},
|
|
31452
|
+
args: { id: {
|
|
31453
|
+
type: "positional",
|
|
31454
|
+
required: true,
|
|
31455
|
+
description: "Group ID"
|
|
31456
|
+
} },
|
|
31457
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31458
|
+
const result = yield* (yield* apiClient).groups.listMembers({ path: { id: args.id } });
|
|
31459
|
+
yield* printHumanTable(["Member ID", "Added"], result.items.map((member) => [member.memberId, member.createdAt]));
|
|
31460
|
+
return result;
|
|
31461
|
+
}), {
|
|
31462
|
+
exits: groupErrorExtras,
|
|
31463
|
+
json: "value"
|
|
31464
|
+
})
|
|
31465
|
+
});
|
|
31466
|
+
const membersCommand = defineCommand({
|
|
31467
|
+
meta: {
|
|
31468
|
+
name: "members",
|
|
31469
|
+
description: "Manage the members of a group"
|
|
31470
|
+
},
|
|
31471
|
+
subCommands: {
|
|
31472
|
+
list: listGroupMembersCommand,
|
|
31473
|
+
add: addGroupMemberCommand,
|
|
31474
|
+
remove: removeGroupMemberCommand
|
|
31475
|
+
}
|
|
31476
|
+
});
|
|
31477
|
+
|
|
31478
|
+
//#endregion
|
|
31479
|
+
//#region src/commands/groups/policies.ts
|
|
31480
|
+
const listGroupPoliciesCommand = defineCommand({
|
|
31481
|
+
meta: {
|
|
31482
|
+
name: "policies",
|
|
31483
|
+
description: "List policies attached to a group"
|
|
31484
|
+
},
|
|
31485
|
+
args: { id: {
|
|
31486
|
+
type: "positional",
|
|
31487
|
+
required: true,
|
|
31488
|
+
description: "Group ID"
|
|
31489
|
+
} },
|
|
31490
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31491
|
+
const result = yield* (yield* apiClient)["policy-attachments"].listForGroup({ path: { id: args.id } });
|
|
31492
|
+
yield* printHumanTable([
|
|
31493
|
+
"Attachment ID",
|
|
31494
|
+
"Policy ID",
|
|
31495
|
+
"Created"
|
|
31496
|
+
], result.items.map((attachment) => [
|
|
31497
|
+
attachment.id,
|
|
31498
|
+
attachment.policyId,
|
|
31499
|
+
attachment.createdAt
|
|
31500
|
+
]));
|
|
31501
|
+
return result;
|
|
31502
|
+
}), {
|
|
31503
|
+
exits: groupErrorExtras,
|
|
31504
|
+
json: "value"
|
|
31505
|
+
})
|
|
31506
|
+
});
|
|
31507
|
+
|
|
31508
|
+
//#endregion
|
|
31509
|
+
//#region src/commands/groups/update.ts
|
|
31510
|
+
const updateGroupCommand = defineCommand({
|
|
31511
|
+
meta: {
|
|
31512
|
+
name: "update",
|
|
31513
|
+
description: "Update a group's name or description"
|
|
31514
|
+
},
|
|
31515
|
+
args: {
|
|
31516
|
+
id: {
|
|
31517
|
+
type: "positional",
|
|
31518
|
+
required: true,
|
|
31519
|
+
description: "Group ID"
|
|
31520
|
+
},
|
|
31521
|
+
name: {
|
|
31522
|
+
type: "string",
|
|
31523
|
+
description: "New display name"
|
|
31524
|
+
},
|
|
31525
|
+
description: {
|
|
31526
|
+
type: "string",
|
|
31527
|
+
description: "New description"
|
|
31528
|
+
}
|
|
31529
|
+
},
|
|
31530
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31531
|
+
const group = yield* (yield* apiClient).groups.update({
|
|
31532
|
+
path: { id: args.id },
|
|
31533
|
+
payload: compact({
|
|
31534
|
+
name: args.name,
|
|
31535
|
+
description: args.description
|
|
31536
|
+
})
|
|
31537
|
+
});
|
|
31538
|
+
yield* printHumanKeyValue([
|
|
31539
|
+
["ID", group.id],
|
|
31540
|
+
["Name", group.name],
|
|
31541
|
+
["Description", group.description ?? "-"],
|
|
31542
|
+
["Updated", group.updatedAt ?? "-"]
|
|
31543
|
+
]);
|
|
31544
|
+
return group;
|
|
31545
|
+
}), {
|
|
31546
|
+
exits: groupErrorExtras,
|
|
31547
|
+
json: "value"
|
|
31548
|
+
})
|
|
31549
|
+
});
|
|
31550
|
+
|
|
31551
|
+
//#endregion
|
|
31552
|
+
//#region src/commands/groups/index.ts
|
|
31553
|
+
const groupsCommand = defineCommand({
|
|
31554
|
+
meta: {
|
|
31555
|
+
name: "groups",
|
|
31556
|
+
description: "Manage member groups for collective policy attachment"
|
|
31557
|
+
},
|
|
31558
|
+
subCommands: {
|
|
31559
|
+
list: listGroupsCommand,
|
|
31560
|
+
create: createGroupCommand,
|
|
31561
|
+
update: updateGroupCommand,
|
|
31562
|
+
delete: deleteGroupCommand,
|
|
31563
|
+
members: membersCommand,
|
|
31564
|
+
policies: listGroupPoliciesCommand,
|
|
31565
|
+
attach: attachGroupPolicyCommand,
|
|
31566
|
+
detach: detachGroupPolicyCommand
|
|
31567
|
+
}
|
|
31568
|
+
});
|
|
31569
|
+
|
|
31302
31570
|
//#endregion
|
|
31303
31571
|
//#region src/commands/init.ts
|
|
31304
31572
|
const checkExistingLink = (api, config, localSlug) => Effect.gen(function* () {
|
|
@@ -31327,7 +31595,7 @@ const readPackageJsonName = (projectRoot) => Effect.gen(function* () {
|
|
|
31327
31595
|
const content = yield* (yield* FileSystem.FileSystem).readFileString(path.join(projectRoot, "package.json")).pipe(Effect.orElseSucceed(() => ""));
|
|
31328
31596
|
if (content.length === 0) return;
|
|
31329
31597
|
const parsed = yield* Effect.try(() => JSON.parse(content)).pipe(Effect.orElseSucceed(() => void 0));
|
|
31330
|
-
const name = isRecord(parsed) ? parsed["name"] : void 0;
|
|
31598
|
+
const name = isRecord$1(parsed) ? parsed["name"] : void 0;
|
|
31331
31599
|
return typeof name === "string" && name.length > 0 ? name : void 0;
|
|
31332
31600
|
});
|
|
31333
31601
|
/**
|
|
@@ -31568,9 +31836,239 @@ const openCommand = defineCommand({
|
|
|
31568
31836
|
}))
|
|
31569
31837
|
});
|
|
31570
31838
|
|
|
31839
|
+
//#endregion
|
|
31840
|
+
//#region src/commands/policies/helpers.ts
|
|
31841
|
+
var PolicyCommandError = class extends Data.TaggedError("PolicyCommandError") {};
|
|
31842
|
+
const policyErrorExtras = { PolicyCommandError: 2 };
|
|
31843
|
+
/** A managed preset id is virtual + read-only; its id is prefixed with `managed:`. */
|
|
31844
|
+
const isManagedPolicyId = (id) => id.startsWith("managed:");
|
|
31845
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
31846
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31847
|
+
/**
|
|
31848
|
+
* Parse + shape-validate a `--document` JSON string client-side so a malformed
|
|
31849
|
+
* document fails with a clear local message before any network round-trip. The
|
|
31850
|
+
* server still re-validates action tokens against the real vocabulary and the
|
|
31851
|
+
* selectors against the shared grammar; this only guards the JSON shape and the
|
|
31852
|
+
* action/selector token SHAPE via the contract's pure validators.
|
|
31853
|
+
*
|
|
31854
|
+
* Expected shape:
|
|
31855
|
+
* { "statements": [ { "effect": "allow"|"deny", "actions": ["project:read"|"*"], "resources": ["*"|"project/A"] } ] }
|
|
31856
|
+
*/
|
|
31857
|
+
const parsePolicyDocument = (raw) => Effect.gen(function* () {
|
|
31858
|
+
const parsed = yield* Effect.try({
|
|
31859
|
+
try: () => JSON.parse(raw),
|
|
31860
|
+
catch: () => new PolicyCommandError({ message: "The --document value is not valid JSON. Pass a JSON object string." })
|
|
31861
|
+
});
|
|
31862
|
+
if (!isRecord(parsed) || !Array.isArray(parsed["statements"])) return yield* new PolicyCommandError({ message: "The document must be a JSON object with a \"statements\" array. Example: {\"statements\":[{\"effect\":\"allow\",\"actions\":[\"project:read\"],\"resources\":[\"*\"]}]}" });
|
|
31863
|
+
const statements = [];
|
|
31864
|
+
for (const [index, entry] of parsed["statements"].entries()) {
|
|
31865
|
+
const statement = yield* validateStatement(entry, index);
|
|
31866
|
+
statements.push(statement);
|
|
31867
|
+
}
|
|
31868
|
+
if (statements.length === 0) return yield* new PolicyCommandError({ message: "The document must contain at least one statement." });
|
|
31869
|
+
return { statements };
|
|
31870
|
+
});
|
|
31871
|
+
const validateStatement = (entry, index) => Effect.gen(function* () {
|
|
31872
|
+
const at = `statements[${index}]`;
|
|
31873
|
+
if (!isRecord(entry)) return yield* new PolicyCommandError({ message: `${at} must be a JSON object.` });
|
|
31874
|
+
const { effect } = entry;
|
|
31875
|
+
if (effect !== "allow" && effect !== "deny") return yield* new PolicyCommandError({ message: `${at}.effect must be "allow" or "deny".` });
|
|
31876
|
+
const { actions } = entry;
|
|
31877
|
+
if (!isStringArray(actions) || actions.length === 0) return yield* new PolicyCommandError({ message: `${at}.actions must be a non-empty array of action-token strings.` });
|
|
31878
|
+
const badAction = actions.find((token) => !isValidActionTokenShape(token));
|
|
31879
|
+
if (badAction !== void 0) return yield* new PolicyCommandError({ message: `${at}.actions has an invalid token "${badAction}". Use "*", "<resource>:*", or "<resource>:<action>".` });
|
|
31880
|
+
const { resources } = entry;
|
|
31881
|
+
if (!isStringArray(resources) || resources.length === 0) return yield* new PolicyCommandError({ message: `${at}.resources must be a non-empty array of selector strings.` });
|
|
31882
|
+
const badResource = resources.find((selector) => !isValidSelector(selector));
|
|
31883
|
+
if (badResource !== void 0) return yield* new PolicyCommandError({ message: `${at}.resources has an invalid selector "${badResource}". Use "*" or slash-joined segments like "project/A".` });
|
|
31884
|
+
const inertResource = resources.find((selector) => !isCanonicalSelector(selector));
|
|
31885
|
+
if (inertResource !== void 0) return yield* new PolicyCommandError({ message: `${at}.resources selector "${inertResource}" matches no known resource path. Use segments like "project/{id}/channel/{id}" or "project/{id}/env/{env}".` });
|
|
31886
|
+
return {
|
|
31887
|
+
effect,
|
|
31888
|
+
actions,
|
|
31889
|
+
resources
|
|
31890
|
+
};
|
|
31891
|
+
});
|
|
31892
|
+
|
|
31893
|
+
//#endregion
|
|
31894
|
+
//#region src/commands/policies/create.ts
|
|
31895
|
+
const DOCUMENT_HELP = "JSON document string: {\"statements\":[{\"effect\":\"allow\"|\"deny\",\"actions\":[\"<resource>:<action>\"|\"<resource>:*\"|\"*\"],\"resources\":[\"*\"|\"project/A\"|\"project/*/env/production\"]}]}";
|
|
31896
|
+
const createPolicyCommand = defineCommand({
|
|
31897
|
+
meta: {
|
|
31898
|
+
name: "create",
|
|
31899
|
+
description: "Create a named IAM policy from a JSON document"
|
|
31900
|
+
},
|
|
31901
|
+
args: {
|
|
31902
|
+
name: {
|
|
31903
|
+
type: "string",
|
|
31904
|
+
required: true,
|
|
31905
|
+
description: "Policy display name"
|
|
31906
|
+
},
|
|
31907
|
+
description: {
|
|
31908
|
+
type: "string",
|
|
31909
|
+
description: "Optional human description"
|
|
31910
|
+
},
|
|
31911
|
+
document: {
|
|
31912
|
+
type: "string",
|
|
31913
|
+
required: true,
|
|
31914
|
+
description: DOCUMENT_HELP
|
|
31915
|
+
}
|
|
31916
|
+
},
|
|
31917
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31918
|
+
const document = yield* parsePolicyDocument(args.document);
|
|
31919
|
+
const policy = yield* (yield* apiClient).policies.create({ payload: {
|
|
31920
|
+
name: args.name,
|
|
31921
|
+
document,
|
|
31922
|
+
...compact({ description: args.description })
|
|
31923
|
+
} });
|
|
31924
|
+
yield* printHumanKeyValue([
|
|
31925
|
+
["ID", policy.id],
|
|
31926
|
+
["Name", policy.name],
|
|
31927
|
+
["Description", policy.description ?? "-"],
|
|
31928
|
+
["Statements", String(policy.document.statements.length)],
|
|
31929
|
+
["Created", policy.createdAt]
|
|
31930
|
+
]);
|
|
31931
|
+
return policy;
|
|
31932
|
+
}), {
|
|
31933
|
+
exits: policyErrorExtras,
|
|
31934
|
+
json: "value"
|
|
31935
|
+
})
|
|
31936
|
+
});
|
|
31937
|
+
|
|
31938
|
+
//#endregion
|
|
31939
|
+
//#region src/commands/policies/delete.ts
|
|
31940
|
+
const deletePolicyCommand = defineCommand({
|
|
31941
|
+
meta: {
|
|
31942
|
+
name: "delete",
|
|
31943
|
+
description: "Delete a policy and sweep its attachments (managed presets cannot be deleted)"
|
|
31944
|
+
},
|
|
31945
|
+
args: {
|
|
31946
|
+
id: {
|
|
31947
|
+
type: "positional",
|
|
31948
|
+
required: true,
|
|
31949
|
+
description: "Policy ID"
|
|
31950
|
+
},
|
|
31951
|
+
yes: {
|
|
31952
|
+
type: "boolean",
|
|
31953
|
+
description: "Skip confirmation prompt"
|
|
31954
|
+
}
|
|
31955
|
+
},
|
|
31956
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31957
|
+
if (isManagedPolicyId(args.id)) return yield* new PolicyCommandError({ message: `Policy "${args.id}" is a managed preset and cannot be deleted.` });
|
|
31958
|
+
if (!args.yes) {
|
|
31959
|
+
if (!(yield* promptConfirm(`Delete policy ${args.id}?`, { initialValue: false }))) {
|
|
31960
|
+
yield* printHuman("Cancelled.");
|
|
31961
|
+
return;
|
|
31962
|
+
}
|
|
31963
|
+
}
|
|
31964
|
+
yield* (yield* apiClient).policies.delete({ path: { id: args.id } });
|
|
31965
|
+
yield* printHuman(`Deleted policy ${args.id}.`);
|
|
31966
|
+
return {
|
|
31967
|
+
id: args.id,
|
|
31968
|
+
deleted: true
|
|
31969
|
+
};
|
|
31970
|
+
}), {
|
|
31971
|
+
exits: policyErrorExtras,
|
|
31972
|
+
json: "value"
|
|
31973
|
+
})
|
|
31974
|
+
});
|
|
31975
|
+
|
|
31976
|
+
//#endregion
|
|
31977
|
+
//#region src/commands/policies/list.ts
|
|
31978
|
+
const listPoliciesCommand = defineCommand({
|
|
31979
|
+
meta: {
|
|
31980
|
+
name: "list",
|
|
31981
|
+
description: "List policies in the active organization (includes read-only managed presets)"
|
|
31982
|
+
},
|
|
31983
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
31984
|
+
const result = yield* (yield* apiClient).policies.list();
|
|
31985
|
+
yield* printHumanTable([
|
|
31986
|
+
"ID",
|
|
31987
|
+
"Name",
|
|
31988
|
+
"Statements",
|
|
31989
|
+
"Managed"
|
|
31990
|
+
], result.items.map((policy) => [
|
|
31991
|
+
policy.id,
|
|
31992
|
+
policy.name,
|
|
31993
|
+
String(policy.document.statements.length),
|
|
31994
|
+
isManagedPolicyId(policy.id) ? "yes" : "no"
|
|
31995
|
+
]));
|
|
31996
|
+
return result;
|
|
31997
|
+
}), {
|
|
31998
|
+
exits: policyErrorExtras,
|
|
31999
|
+
json: "value"
|
|
32000
|
+
})
|
|
32001
|
+
});
|
|
32002
|
+
|
|
32003
|
+
//#endregion
|
|
32004
|
+
//#region src/commands/policies/update.ts
|
|
32005
|
+
const updatePolicyCommand = defineCommand({
|
|
32006
|
+
meta: {
|
|
32007
|
+
name: "update",
|
|
32008
|
+
description: "Update a policy's name, description, or document (managed presets are read-only)"
|
|
32009
|
+
},
|
|
32010
|
+
args: {
|
|
32011
|
+
id: {
|
|
32012
|
+
type: "positional",
|
|
32013
|
+
required: true,
|
|
32014
|
+
description: "Policy ID"
|
|
32015
|
+
},
|
|
32016
|
+
name: {
|
|
32017
|
+
type: "string",
|
|
32018
|
+
description: "New display name"
|
|
32019
|
+
},
|
|
32020
|
+
description: {
|
|
32021
|
+
type: "string",
|
|
32022
|
+
description: "New description"
|
|
32023
|
+
},
|
|
32024
|
+
document: {
|
|
32025
|
+
type: "string",
|
|
32026
|
+
description: "Replacement JSON document string"
|
|
32027
|
+
}
|
|
32028
|
+
},
|
|
32029
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
32030
|
+
if (isManagedPolicyId(args.id)) return yield* new PolicyCommandError({ message: `Policy "${args.id}" is a managed preset and is read-only. Create a custom policy instead.` });
|
|
32031
|
+
const document = args.document === void 0 ? void 0 : yield* parsePolicyDocument(args.document);
|
|
32032
|
+
const policy = yield* (yield* apiClient).policies.update({
|
|
32033
|
+
path: { id: args.id },
|
|
32034
|
+
payload: compact({
|
|
32035
|
+
name: args.name,
|
|
32036
|
+
description: args.description,
|
|
32037
|
+
document
|
|
32038
|
+
})
|
|
32039
|
+
});
|
|
32040
|
+
yield* printHumanKeyValue([
|
|
32041
|
+
["ID", policy.id],
|
|
32042
|
+
["Name", policy.name],
|
|
32043
|
+
["Description", policy.description ?? "-"],
|
|
32044
|
+
["Statements", String(policy.document.statements.length)],
|
|
32045
|
+
["Updated", policy.updatedAt ?? "-"]
|
|
32046
|
+
]);
|
|
32047
|
+
return policy;
|
|
32048
|
+
}), {
|
|
32049
|
+
exits: policyErrorExtras,
|
|
32050
|
+
json: "value"
|
|
32051
|
+
})
|
|
32052
|
+
});
|
|
32053
|
+
|
|
32054
|
+
//#endregion
|
|
32055
|
+
//#region src/commands/policies/index.ts
|
|
32056
|
+
const policiesCommand = defineCommand({
|
|
32057
|
+
meta: {
|
|
32058
|
+
name: "policies",
|
|
32059
|
+
description: "Manage IAM policies (named, reusable permission grants)"
|
|
32060
|
+
},
|
|
32061
|
+
subCommands: {
|
|
32062
|
+
list: listPoliciesCommand,
|
|
32063
|
+
create: createPolicyCommand,
|
|
32064
|
+
update: updatePolicyCommand,
|
|
32065
|
+
delete: deletePolicyCommand
|
|
32066
|
+
}
|
|
32067
|
+
});
|
|
32068
|
+
|
|
31571
32069
|
//#endregion
|
|
31572
32070
|
//#region src/commands/projects.ts
|
|
31573
|
-
const listCommand$
|
|
32071
|
+
const listCommand$1 = defineCommand({
|
|
31574
32072
|
meta: {
|
|
31575
32073
|
name: "list",
|
|
31576
32074
|
description: "List projects (most recently active first)"
|
|
@@ -31621,7 +32119,7 @@ const listCommand$2 = defineCommand({
|
|
|
31621
32119
|
yield* printHuman(`Page ${result.page} · ${result.items.length} of ${result.total} project(s)`);
|
|
31622
32120
|
}))
|
|
31623
32121
|
});
|
|
31624
|
-
const createCommand
|
|
32122
|
+
const createCommand = defineCommand({
|
|
31625
32123
|
meta: {
|
|
31626
32124
|
name: "create",
|
|
31627
32125
|
description: "Create a new project"
|
|
@@ -31699,7 +32197,7 @@ const renameCommand = defineCommand({
|
|
|
31699
32197
|
return project;
|
|
31700
32198
|
}), { json: "value" })
|
|
31701
32199
|
});
|
|
31702
|
-
const deleteCommand$
|
|
32200
|
+
const deleteCommand$1 = defineCommand({
|
|
31703
32201
|
meta: {
|
|
31704
32202
|
name: "delete",
|
|
31705
32203
|
description: "Delete a project"
|
|
@@ -31723,230 +32221,11 @@ const projectsCommand = defineCommand({
|
|
|
31723
32221
|
name: "projects",
|
|
31724
32222
|
description: "Manage projects"
|
|
31725
32223
|
},
|
|
31726
|
-
subCommands: {
|
|
31727
|
-
list: listCommand$2,
|
|
31728
|
-
create: createCommand$1,
|
|
31729
|
-
get: getCommand,
|
|
31730
|
-
rename: renameCommand,
|
|
31731
|
-
delete: deleteCommand$2
|
|
31732
|
-
}
|
|
31733
|
-
});
|
|
31734
|
-
|
|
31735
|
-
//#endregion
|
|
31736
|
-
//#region src/commands/roles/helpers.ts
|
|
31737
|
-
var RoleCommandError = class extends Data.TaggedError("RoleCommandError") {};
|
|
31738
|
-
const roleErrorExtras = { RoleCommandError: 2 };
|
|
31739
|
-
/**
|
|
31740
|
-
* Parse comma-separated "resource:action" tokens into the PermissionGrant array
|
|
31741
|
-
* the API expects. Multiple actions for the same resource are grouped.
|
|
31742
|
-
*
|
|
31743
|
-
* Input examples:
|
|
31744
|
-
* "channel:read,channel:update"
|
|
31745
|
-
* "channel:read, rollout:create, rollout:update"
|
|
31746
|
-
*/
|
|
31747
|
-
const parsePermissionTokens = (raw) => Effect.gen(function* () {
|
|
31748
|
-
const tokens = raw.split(",").map((tok) => tok.trim()).filter((tok) => tok.length > 0);
|
|
31749
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
31750
|
-
for (const token of tokens) {
|
|
31751
|
-
const colonIdx = token.indexOf(":");
|
|
31752
|
-
if (colonIdx === -1) return yield* new RoleCommandError({ message: `Invalid permission token "${token}" — expected "resource:action" format.` });
|
|
31753
|
-
const resource = token.slice(0, colonIdx).trim();
|
|
31754
|
-
const action = token.slice(colonIdx + 1).trim();
|
|
31755
|
-
if (!resource || !action) return yield* new RoleCommandError({ message: `Invalid permission token "${token}" — resource and action must be non-empty.` });
|
|
31756
|
-
const actions = grouped.get(resource) ?? /* @__PURE__ */ new Set();
|
|
31757
|
-
actions.add(action);
|
|
31758
|
-
grouped.set(resource, actions);
|
|
31759
|
-
}
|
|
31760
|
-
return [...grouped.entries()].map(([resource, actions]) => ({
|
|
31761
|
-
resource,
|
|
31762
|
-
actions: [...actions]
|
|
31763
|
-
}));
|
|
31764
|
-
});
|
|
31765
|
-
|
|
31766
|
-
//#endregion
|
|
31767
|
-
//#region src/commands/roles/create.ts
|
|
31768
|
-
const createCommand = defineCommand({
|
|
31769
|
-
meta: {
|
|
31770
|
-
name: "create",
|
|
31771
|
-
description: "Create a custom role"
|
|
31772
|
-
},
|
|
31773
|
-
args: {
|
|
31774
|
-
name: {
|
|
31775
|
-
type: "string",
|
|
31776
|
-
required: true,
|
|
31777
|
-
description: "Role name (unique per organization)"
|
|
31778
|
-
},
|
|
31779
|
-
permission: {
|
|
31780
|
-
type: "string",
|
|
31781
|
-
required: true,
|
|
31782
|
-
description: "Permission tokens in resource:action format, comma-separated (e.g. channel:read,channel:update)"
|
|
31783
|
-
}
|
|
31784
|
-
},
|
|
31785
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31786
|
-
const permissions = yield* parsePermissionTokens(args.permission);
|
|
31787
|
-
const role = yield* (yield* apiClient).roles.create({ payload: {
|
|
31788
|
-
name: args.name,
|
|
31789
|
-
permissions
|
|
31790
|
-
} });
|
|
31791
|
-
yield* printHumanKeyValue([
|
|
31792
|
-
["ID", role.id],
|
|
31793
|
-
["Name", role.role],
|
|
31794
|
-
["Permissions", role.permissions.map((perm) => `${perm.resource}:[${perm.actions.join(",")}]`).join("; ")],
|
|
31795
|
-
["Created", role.createdAt]
|
|
31796
|
-
]);
|
|
31797
|
-
return role;
|
|
31798
|
-
}), {
|
|
31799
|
-
exits: roleErrorExtras,
|
|
31800
|
-
json: "value"
|
|
31801
|
-
})
|
|
31802
|
-
});
|
|
31803
|
-
|
|
31804
|
-
//#endregion
|
|
31805
|
-
//#region src/commands/roles/delete.ts
|
|
31806
|
-
const deleteCommand$1 = defineCommand({
|
|
31807
|
-
meta: {
|
|
31808
|
-
name: "delete",
|
|
31809
|
-
description: "Delete a custom role"
|
|
31810
|
-
},
|
|
31811
|
-
args: {
|
|
31812
|
-
id: {
|
|
31813
|
-
type: "positional",
|
|
31814
|
-
required: true,
|
|
31815
|
-
description: "Role ID"
|
|
31816
|
-
},
|
|
31817
|
-
yes: {
|
|
31818
|
-
type: "boolean",
|
|
31819
|
-
description: "Skip confirmation prompt"
|
|
31820
|
-
}
|
|
31821
|
-
},
|
|
31822
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31823
|
-
if (!args.yes) {
|
|
31824
|
-
if (!(yield* promptConfirm(`Delete role ${args.id}?`, { initialValue: false }))) {
|
|
31825
|
-
yield* printHuman("Cancelled.");
|
|
31826
|
-
return { deleted: 0 };
|
|
31827
|
-
}
|
|
31828
|
-
}
|
|
31829
|
-
const result = yield* (yield* apiClient).roles.delete({ path: { id: args.id } });
|
|
31830
|
-
yield* printHuman(`Role ${args.id} deleted.`);
|
|
31831
|
-
return result;
|
|
31832
|
-
}), {
|
|
31833
|
-
exits: roleErrorExtras,
|
|
31834
|
-
json: "value"
|
|
31835
|
-
})
|
|
31836
|
-
});
|
|
31837
|
-
|
|
31838
|
-
//#endregion
|
|
31839
|
-
//#region src/commands/roles/list.ts
|
|
31840
|
-
const listCommand$1 = defineCommand({
|
|
31841
|
-
meta: {
|
|
31842
|
-
name: "list",
|
|
31843
|
-
description: "List custom roles for the active organization"
|
|
31844
|
-
},
|
|
31845
|
-
args: { "organization-id": {
|
|
31846
|
-
type: "string",
|
|
31847
|
-
required: true,
|
|
31848
|
-
description: "Organization ID to list roles for"
|
|
31849
|
-
} },
|
|
31850
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31851
|
-
yield* printList([
|
|
31852
|
-
"ID",
|
|
31853
|
-
"Name",
|
|
31854
|
-
"Permissions",
|
|
31855
|
-
"Created"
|
|
31856
|
-
], (yield* (yield* apiClient).roles.list({ urlParams: { organizationId: args["organization-id"] } })).map((role) => [
|
|
31857
|
-
role.id,
|
|
31858
|
-
role.role,
|
|
31859
|
-
role.permissions.map((perm) => `${perm.resource}:[${perm.actions.join(",")}]`).join("; "),
|
|
31860
|
-
role.createdAt
|
|
31861
|
-
]), "No custom roles found.");
|
|
31862
|
-
}), roleErrorExtras)
|
|
31863
|
-
});
|
|
31864
|
-
|
|
31865
|
-
//#endregion
|
|
31866
|
-
//#region src/commands/roles/update.ts
|
|
31867
|
-
const updateCommand$1 = defineCommand({
|
|
31868
|
-
meta: {
|
|
31869
|
-
name: "update",
|
|
31870
|
-
description: "Update a custom role's name or permissions"
|
|
31871
|
-
},
|
|
31872
|
-
args: {
|
|
31873
|
-
id: {
|
|
31874
|
-
type: "positional",
|
|
31875
|
-
required: true,
|
|
31876
|
-
description: "Role ID"
|
|
31877
|
-
},
|
|
31878
|
-
name: {
|
|
31879
|
-
type: "string",
|
|
31880
|
-
description: "New role name"
|
|
31881
|
-
},
|
|
31882
|
-
permission: {
|
|
31883
|
-
type: "string",
|
|
31884
|
-
description: "Replacement permission tokens in resource:action format, comma-separated (e.g. channel:read,channel:update)"
|
|
31885
|
-
}
|
|
31886
|
-
},
|
|
31887
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31888
|
-
const permissions = args.permission === void 0 ? void 0 : yield* parsePermissionTokens(args.permission);
|
|
31889
|
-
const role = yield* (yield* apiClient).roles.update({
|
|
31890
|
-
path: { id: args.id },
|
|
31891
|
-
payload: compact({
|
|
31892
|
-
name: args.name,
|
|
31893
|
-
permissions
|
|
31894
|
-
})
|
|
31895
|
-
});
|
|
31896
|
-
yield* printHumanKeyValue([
|
|
31897
|
-
["ID", role.id],
|
|
31898
|
-
["Name", role.role],
|
|
31899
|
-
["Permissions", role.permissions.map((perm) => `${perm.resource}:[${perm.actions.join(",")}]`).join("; ")],
|
|
31900
|
-
["Updated", role.updatedAt ?? "-"]
|
|
31901
|
-
]);
|
|
31902
|
-
return role;
|
|
31903
|
-
}), {
|
|
31904
|
-
exits: roleErrorExtras,
|
|
31905
|
-
json: "value"
|
|
31906
|
-
})
|
|
31907
|
-
});
|
|
31908
|
-
|
|
31909
|
-
//#endregion
|
|
31910
|
-
//#region src/commands/roles/view.ts
|
|
31911
|
-
const viewCommand$1 = defineCommand({
|
|
31912
|
-
meta: {
|
|
31913
|
-
name: "view",
|
|
31914
|
-
description: "Show a custom role by ID"
|
|
31915
|
-
},
|
|
31916
|
-
args: { id: {
|
|
31917
|
-
type: "positional",
|
|
31918
|
-
required: true,
|
|
31919
|
-
description: "Role ID"
|
|
31920
|
-
} },
|
|
31921
|
-
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
31922
|
-
const role = yield* (yield* apiClient).roles.get({ path: { id: args.id } });
|
|
31923
|
-
yield* printHumanKeyValue([
|
|
31924
|
-
["ID", role.id],
|
|
31925
|
-
["Name", role.role],
|
|
31926
|
-
["Organization ID", role.organizationId],
|
|
31927
|
-
["Permissions", role.permissions.map((perm) => `${perm.resource}:[${perm.actions.join(",")}]`).join("; ")],
|
|
31928
|
-
["Created", role.createdAt],
|
|
31929
|
-
["Updated", role.updatedAt ?? "-"]
|
|
31930
|
-
]);
|
|
31931
|
-
return role;
|
|
31932
|
-
}), {
|
|
31933
|
-
exits: roleErrorExtras,
|
|
31934
|
-
json: "value"
|
|
31935
|
-
})
|
|
31936
|
-
});
|
|
31937
|
-
|
|
31938
|
-
//#endregion
|
|
31939
|
-
//#region src/commands/roles/index.ts
|
|
31940
|
-
const rolesCommand = defineCommand({
|
|
31941
|
-
meta: {
|
|
31942
|
-
name: "roles",
|
|
31943
|
-
description: "Manage custom organization roles"
|
|
31944
|
-
},
|
|
31945
32224
|
subCommands: {
|
|
31946
32225
|
list: listCommand$1,
|
|
31947
|
-
view: viewCommand$1,
|
|
31948
32226
|
create: createCommand,
|
|
31949
|
-
|
|
32227
|
+
get: getCommand,
|
|
32228
|
+
rename: renameCommand,
|
|
31950
32229
|
delete: deleteCommand$1
|
|
31951
32230
|
}
|
|
31952
32231
|
});
|
|
@@ -33423,10 +33702,10 @@ const assertSignedManifestBundleUrl = (params) => Effect.gen(function* () {
|
|
|
33423
33702
|
try: () => JSON.parse(params.manifestBody),
|
|
33424
33703
|
catch: () => params.makeError(`Signed ${params.platform} manifestBody is not valid JSON.`)
|
|
33425
33704
|
});
|
|
33426
|
-
if (!isRecord(parsed)) return yield* Effect.fail(params.makeError(`Signed ${params.platform} manifestBody must be a JSON object.`));
|
|
33705
|
+
if (!isRecord$1(parsed)) return yield* Effect.fail(params.makeError(`Signed ${params.platform} manifestBody must be a JSON object.`));
|
|
33427
33706
|
const { id } = parsed;
|
|
33428
33707
|
const { launchAsset } = parsed;
|
|
33429
|
-
if (typeof id !== "string" || !isRecord(launchAsset)) return yield* Effect.fail(params.makeError(`Signed ${params.platform} manifestBody must carry a string "id" and an object "launchAsset".`));
|
|
33708
|
+
if (typeof id !== "string" || !isRecord$1(launchAsset)) return yield* Effect.fail(params.makeError(`Signed ${params.platform} manifestBody must carry a string "id" and an object "launchAsset".`));
|
|
33430
33709
|
const { url } = launchAsset;
|
|
33431
33710
|
const expectedPrefix = `${params.serverBaseUrl}/manifest/${params.projectId}/bundle/${id}/`;
|
|
33432
33711
|
if (typeof url !== "string" || !url.startsWith(expectedPrefix)) return yield* Effect.fail(params.makeError(`Signed ${params.platform} manifestBody launchAsset.url must point at the Worker bundle route (${expectedPrefix}…) so bsdiff patches apply; got ${typeof url === "string" ? url : "a non-string value"}. Re-render the signed manifest with the Worker bundle URL.`));
|
|
@@ -34354,10 +34633,10 @@ const extractDirectiveCommitTime = (directiveBody) => Effect.gen(function* () {
|
|
|
34354
34633
|
try: () => JSON.parse(directiveBody),
|
|
34355
34634
|
catch: () => new UpdateRollbackError({ message: "directiveBody must be valid JSON." })
|
|
34356
34635
|
});
|
|
34357
|
-
if (!isRecord(directive)) return yield* new UpdateRollbackError({ message: "directiveBody must decode to a JSON object." });
|
|
34636
|
+
if (!isRecord$1(directive)) return yield* new UpdateRollbackError({ message: "directiveBody must decode to a JSON object." });
|
|
34358
34637
|
if (directive["type"] !== "rollBackToEmbedded") return yield* new UpdateRollbackError({ message: "directiveBody.type must be \"rollBackToEmbedded\"." });
|
|
34359
34638
|
const { parameters } = directive;
|
|
34360
|
-
if (!isRecord(parameters)) return yield* new UpdateRollbackError({ message: "directiveBody.parameters must be an object." });
|
|
34639
|
+
if (!isRecord$1(parameters)) return yield* new UpdateRollbackError({ message: "directiveBody.parameters must be an object." });
|
|
34361
34640
|
const { commitTime } = parameters;
|
|
34362
34641
|
if (typeof commitTime !== "string" || Number.isNaN(Date.parse(commitTime))) return yield* new UpdateRollbackError({ message: "directiveBody.parameters.commitTime must be a valid ISO 8601 timestamp." });
|
|
34363
34642
|
if (new Date(Date.parse(commitTime)).toISOString() !== commitTime) return yield* new UpdateRollbackError({ message: "directiveBody.parameters.commitTime must be canonical ISO 8601 with milliseconds and a trailing Z (e.g. 2026-05-06T14:00:00.000Z). The expo-updates client cannot parse other forms (no milliseconds, numeric timezone offsets), so the signed rollback would be rejected on-device." });
|
|
@@ -35185,9 +35464,10 @@ const commandRegistry = {
|
|
|
35185
35464
|
init: initCommand,
|
|
35186
35465
|
status: statusCommand,
|
|
35187
35466
|
projects: projectsCommand,
|
|
35467
|
+
policies: policiesCommand,
|
|
35468
|
+
groups: groupsCommand,
|
|
35188
35469
|
branches: branchesCommand,
|
|
35189
35470
|
channels: channelsCommand,
|
|
35190
|
-
roles: rolesCommand,
|
|
35191
35471
|
build: buildCommand,
|
|
35192
35472
|
builds: buildsCommand,
|
|
35193
35473
|
credentials: credentialsCommand,
|