@kikimoradev/kcloud 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +1143 -162
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -282,14 +282,14 @@ const quietPrompter = (prompter, warn) => ({
|
|
|
282
282
|
//#region ../../packages/cli-kit/src/table.ts
|
|
283
283
|
const columnGap = " ";
|
|
284
284
|
const visibleWidth = (text) => stripVTControlCharacters(text).length;
|
|
285
|
-
const pad = (text, width, align) => {
|
|
285
|
+
const pad$1 = (text, width, align) => {
|
|
286
286
|
const padding = " ".repeat(Math.max(0, width - visibleWidth(text)));
|
|
287
287
|
return align === "right" ? `${padding}${text}` : `${text}${padding}`;
|
|
288
288
|
};
|
|
289
289
|
const alignRows = (rows, alignments = []) => {
|
|
290
290
|
const columnCount = Math.max(0, ...rows.map((row) => row.length));
|
|
291
291
|
const widths = Array.from({ length: columnCount }, (_, index) => Math.max(0, ...rows.map((row) => visibleWidth(row[index] ?? ""))));
|
|
292
|
-
return rows.map((row) => widths.map((width, index) => pad(row[index] ?? "", width, alignments[index] ?? "left")).join(columnGap).trimEnd()).join("\n");
|
|
292
|
+
return rows.map((row) => widths.map((width, index) => pad$1(row[index] ?? "", width, alignments[index] ?? "left")).join(columnGap).trimEnd()).join("\n");
|
|
293
293
|
};
|
|
294
294
|
const renderTable = (columns, rows) => alignRows([columns.map(({ header }) => header), ...rows], columns.map(({ align }) => align ?? "left"));
|
|
295
295
|
//#endregion
|
|
@@ -635,10 +635,60 @@ const pageSchema = (itemSchema) => z.object({
|
|
|
635
635
|
const credentialKinds = [
|
|
636
636
|
"anthropic_api_key",
|
|
637
637
|
"claude_oauth_token",
|
|
638
|
-
"github_pat"
|
|
638
|
+
"github_pat",
|
|
639
|
+
"gitlab_token",
|
|
640
|
+
"bitbucket_token"
|
|
639
641
|
];
|
|
640
642
|
const credentialKindSchema = z.enum(credentialKinds);
|
|
641
|
-
|
|
643
|
+
const claudeCredentialKinds = ["anthropic_api_key", "claude_oauth_token"];
|
|
644
|
+
const claudeCredentialKindSchema = z.enum(claudeCredentialKinds);
|
|
645
|
+
const gitProviders = [
|
|
646
|
+
"github",
|
|
647
|
+
"gitlab",
|
|
648
|
+
"bitbucket"
|
|
649
|
+
];
|
|
650
|
+
const gitProviderSchema = z.enum(gitProviders);
|
|
651
|
+
const gitCredentialKinds = [
|
|
652
|
+
"github_pat",
|
|
653
|
+
"gitlab_token",
|
|
654
|
+
"bitbucket_token"
|
|
655
|
+
];
|
|
656
|
+
z.enum(gitCredentialKinds);
|
|
657
|
+
const gitCredentialKindOf = {
|
|
658
|
+
github: "github_pat",
|
|
659
|
+
gitlab: "gitlab_token",
|
|
660
|
+
bitbucket: "bitbucket_token"
|
|
661
|
+
};
|
|
662
|
+
const defaultServerUrls = {
|
|
663
|
+
github: "https://github.com",
|
|
664
|
+
gitlab: "https://gitlab.com",
|
|
665
|
+
bitbucket: "https://bitbucket.org"
|
|
666
|
+
};
|
|
667
|
+
const isGitCredentialKind = (kind) => gitCredentialKinds.includes(kind);
|
|
668
|
+
const providerOfKind = (kind) => gitProviders.find((provider) => gitCredentialKindOf[provider] === kind) ?? null;
|
|
669
|
+
const maxServerUrlLength = 2048;
|
|
670
|
+
const parsedServerUrl = (value) => {
|
|
671
|
+
try {
|
|
672
|
+
return new URL(value);
|
|
673
|
+
} catch {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
const isWellFormedServerUrl = (value) => {
|
|
678
|
+
const url = parsedServerUrl(value);
|
|
679
|
+
return url !== null && (url.protocol === "https:" || url.protocol === "http:") && url.hostname !== "" && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && !value.includes("?") && !value.includes("#");
|
|
680
|
+
};
|
|
681
|
+
const gitServerUrlSchema = z.string().trim().min(1).max(maxServerUrlLength).refine(isWellFormedServerUrl, { message: "expected an http or https address without credentials, query or fragment" });
|
|
682
|
+
const providerTokenKindSchema = z.enum([
|
|
683
|
+
"gitlab_personal",
|
|
684
|
+
"gitlab_group",
|
|
685
|
+
"gitlab_project",
|
|
686
|
+
"bitbucket_api_token",
|
|
687
|
+
"bitbucket_access_token",
|
|
688
|
+
"bitbucket_http_token"
|
|
689
|
+
]);
|
|
690
|
+
const providerFailureSchema = z.enum(["rejected", "unreachable"]);
|
|
691
|
+
const identitySourceSchema = z.enum(["provider", "user"]);
|
|
642
692
|
const verificationStatusSchema = z.enum([
|
|
643
693
|
"pending",
|
|
644
694
|
"valid",
|
|
@@ -661,7 +711,14 @@ const credentialMetadataSchema = z.object({
|
|
|
661
711
|
commitEmail: z.string().optional(),
|
|
662
712
|
scopes: z.array(z.string()).optional(),
|
|
663
713
|
checkedAt: z.iso.datetime().optional(),
|
|
664
|
-
failureReason: z.string().optional()
|
|
714
|
+
failureReason: z.string().optional(),
|
|
715
|
+
serverUrl: z.string().optional(),
|
|
716
|
+
accountName: z.string().optional(),
|
|
717
|
+
accountUrl: z.string().optional(),
|
|
718
|
+
accountEmail: z.string().optional(),
|
|
719
|
+
providerTokenKind: providerTokenKindSchema.optional(),
|
|
720
|
+
providerFailure: providerFailureSchema.optional(),
|
|
721
|
+
identitySource: identitySourceSchema.optional()
|
|
665
722
|
}).meta({ id: "CredentialMetadata" });
|
|
666
723
|
const usageWindowStatusSchema = z.enum([
|
|
667
724
|
"allowed",
|
|
@@ -679,9 +736,14 @@ const subscriptionUsageSchema = z.object({
|
|
|
679
736
|
readAt: z.iso.datetime(),
|
|
680
737
|
stale: z.boolean()
|
|
681
738
|
}).meta({ id: "SubscriptionUsage" });
|
|
739
|
+
const claudeAccountQueuePlaceSchema = z.object({
|
|
740
|
+
projectId: idSchema,
|
|
741
|
+
projectName: z.string(),
|
|
742
|
+
position: z.int().positive()
|
|
743
|
+
}).meta({ id: "ClaudeAccountQueuePlace" });
|
|
682
744
|
const credentialSchema = z.object({
|
|
683
745
|
id: idSchema,
|
|
684
|
-
projectId: idSchema,
|
|
746
|
+
projectId: idSchema.nullable(),
|
|
685
747
|
version: z.int().nonnegative(),
|
|
686
748
|
kind: credentialKindSchema,
|
|
687
749
|
label: z.string(),
|
|
@@ -691,23 +753,54 @@ const credentialSchema = z.object({
|
|
|
691
753
|
expiresAt: z.iso.datetime().nullable(),
|
|
692
754
|
expiresAtComputed: z.boolean(),
|
|
693
755
|
usage: subscriptionUsageSchema.nullable(),
|
|
756
|
+
queues: z.array(claudeAccountQueuePlaceSchema),
|
|
757
|
+
creditsExhaustedAt: z.iso.datetime().nullable(),
|
|
758
|
+
limitedUntil: z.iso.datetime().nullable(),
|
|
694
759
|
createdAt: z.iso.datetime(),
|
|
695
760
|
updatedAt: z.iso.datetime()
|
|
696
761
|
}).meta({ id: "Credential" });
|
|
697
762
|
z.object({ items: z.array(credentialSchema) }).meta({ id: "CredentialList" });
|
|
698
763
|
const credentialSummarySchema = z.object({
|
|
699
764
|
id: idSchema,
|
|
700
|
-
projectId: idSchema,
|
|
765
|
+
projectId: idSchema.nullable(),
|
|
701
766
|
kind: credentialKindSchema,
|
|
702
767
|
label: z.string(),
|
|
703
768
|
commitName: z.string().nullable(),
|
|
704
|
-
commitEmail: z.string().nullable()
|
|
769
|
+
commitEmail: z.string().nullable(),
|
|
770
|
+
provider: gitProviderSchema.nullable(),
|
|
771
|
+
serverUrl: z.string().nullable()
|
|
705
772
|
}).meta({ id: "CredentialSummary" });
|
|
773
|
+
const gitCredentialSummarySchema = credentialSummarySchema.extend({
|
|
774
|
+
provider: gitProviderSchema,
|
|
775
|
+
serverUrl: z.string()
|
|
776
|
+
}).meta({ id: "GitCredentialSummary" });
|
|
777
|
+
const commitNameSchema = z.string().trim().min(1).max(255);
|
|
778
|
+
const commitEmailSchema = z.email().max(255);
|
|
779
|
+
const accountEmailSchema = z.email().max(255);
|
|
780
|
+
const hasWholeIdentity = (body) => body.commitName === void 0 === (body.commitEmail === void 0);
|
|
781
|
+
const carriesGitFields = (body) => body.serverUrl !== void 0 || body.accountEmail !== void 0 || body.commitName !== void 0 || body.commitEmail !== void 0;
|
|
706
782
|
z.object({
|
|
707
783
|
kind: credentialKindSchema,
|
|
708
784
|
label: credentialLabelSchema,
|
|
709
|
-
secret: credentialSecretSchema
|
|
785
|
+
secret: credentialSecretSchema,
|
|
786
|
+
serverUrl: gitServerUrlSchema.optional(),
|
|
787
|
+
accountEmail: accountEmailSchema.optional(),
|
|
788
|
+
commitName: commitNameSchema.optional(),
|
|
789
|
+
commitEmail: commitEmailSchema.optional()
|
|
790
|
+
}).refine(hasWholeIdentity, {
|
|
791
|
+
message: "expected both the commit name and the commit email, or neither",
|
|
792
|
+
path: ["commitEmail"]
|
|
793
|
+
}).refine((body) => isGitCredentialKind(body.kind) || !carriesGitFields(body), {
|
|
794
|
+
message: "expected no server, account or commit identity for a Claude credential",
|
|
795
|
+
path: ["kind"]
|
|
796
|
+
}).refine((body) => body.kind !== "github_pat" || body.serverUrl === void 0, {
|
|
797
|
+
message: "expected no server address for a GitHub token",
|
|
798
|
+
path: ["serverUrl"]
|
|
710
799
|
}).meta({ id: "CreateCredentialBody" });
|
|
800
|
+
z.object({
|
|
801
|
+
commitName: commitNameSchema,
|
|
802
|
+
commitEmail: commitEmailSchema
|
|
803
|
+
}).meta({ id: "SetCredentialCommitIdentityBody" });
|
|
711
804
|
z.object({ label: credentialLabelSchema }).meta({ id: "RenameCredentialBody" });
|
|
712
805
|
z.object({
|
|
713
806
|
secret: credentialSecretSchema,
|
|
@@ -717,6 +810,57 @@ z.object({
|
|
|
717
810
|
id: idSchema,
|
|
718
811
|
credentialId: idSchema
|
|
719
812
|
});
|
|
813
|
+
const switchThresholdSchema = z.int().min(1).max(100);
|
|
814
|
+
z.object({ items: z.array(credentialSchema) }).meta({ id: "ClaudeAccountList" });
|
|
815
|
+
z.object({
|
|
816
|
+
kind: claudeCredentialKindSchema,
|
|
817
|
+
label: credentialLabelSchema,
|
|
818
|
+
secret: credentialSecretSchema
|
|
819
|
+
}).meta({ id: "CreateClaudeAccountBody" });
|
|
820
|
+
z.object({
|
|
821
|
+
organizationId: idSchema,
|
|
822
|
+
credentialId: idSchema
|
|
823
|
+
});
|
|
824
|
+
const projectClaudeAccountStateSchema = z.enum([
|
|
825
|
+
"inUse",
|
|
826
|
+
"available",
|
|
827
|
+
"exhausted",
|
|
828
|
+
"noCredits",
|
|
829
|
+
"invalid"
|
|
830
|
+
]);
|
|
831
|
+
const claudeAccountWaitReasonSchema = z.enum([
|
|
832
|
+
"exhausted",
|
|
833
|
+
"blocked",
|
|
834
|
+
"empty"
|
|
835
|
+
]);
|
|
836
|
+
z.enum([
|
|
837
|
+
"queue",
|
|
838
|
+
"threshold",
|
|
839
|
+
"limit",
|
|
840
|
+
"reset",
|
|
841
|
+
"credits",
|
|
842
|
+
"invalid",
|
|
843
|
+
"removed"
|
|
844
|
+
]);
|
|
845
|
+
const projectClaudeAccountEntrySchema = z.object({
|
|
846
|
+
credential: credentialSchema,
|
|
847
|
+
position: z.int().positive(),
|
|
848
|
+
state: projectClaudeAccountStateSchema,
|
|
849
|
+
until: z.iso.datetime().nullable(),
|
|
850
|
+
agents: z.int().nonnegative()
|
|
851
|
+
}).meta({ id: "ProjectClaudeAccountEntry" });
|
|
852
|
+
z.object({
|
|
853
|
+
entries: z.array(projectClaudeAccountEntrySchema),
|
|
854
|
+
activeCredentialId: idSchema.nullable(),
|
|
855
|
+
nearestResetAt: z.iso.datetime().nullable(),
|
|
856
|
+
waitReason: claudeAccountWaitReasonSchema.nullable(),
|
|
857
|
+
version: z.int().nonnegative()
|
|
858
|
+
}).meta({ id: "ProjectClaudeAccounts" });
|
|
859
|
+
const hasUniqueCredentials = (ids) => new Set(ids).size === ids.length;
|
|
860
|
+
z.object({
|
|
861
|
+
credentialIds: z.array(idSchema).max(50).refine(hasUniqueCredentials, { message: "expected every account once" }),
|
|
862
|
+
version: z.int().nonnegative()
|
|
863
|
+
}).meta({ id: "SetProjectClaudeAccountsBody" });
|
|
720
864
|
const errorCodes = Object.keys({
|
|
721
865
|
"validation.failed": "validation",
|
|
722
866
|
"http.route_not_found": "not_found",
|
|
@@ -760,10 +904,18 @@ const errorCodes = Object.keys({
|
|
|
760
904
|
"credential.kind_mismatch": "rule_violation",
|
|
761
905
|
"credential.rejected": "rule_violation",
|
|
762
906
|
"credential.usage_unavailable": "rule_violation",
|
|
907
|
+
"credential.server_refused": "validation",
|
|
908
|
+
"credential.server_mismatch": "rule_violation",
|
|
909
|
+
"account.not_found": "not_found",
|
|
910
|
+
"account.label_taken": "conflict",
|
|
911
|
+
"account.in_queue": "conflict",
|
|
912
|
+
"account.queue_empty": "rule_violation",
|
|
913
|
+
"account.kind_mismatch": "rule_violation",
|
|
763
914
|
"repository.not_found": "not_found",
|
|
764
915
|
"repository.already_registered": "conflict",
|
|
765
916
|
"repository.in_use": "conflict",
|
|
766
917
|
"repository.unreachable": "rule_violation",
|
|
918
|
+
"repository.catalog_unavailable": "rule_violation",
|
|
767
919
|
"tool.not_found": "not_found",
|
|
768
920
|
"tool.name_taken": "conflict",
|
|
769
921
|
"tool.slug_taken": "conflict",
|
|
@@ -782,6 +934,8 @@ const errorCodes = Object.keys({
|
|
|
782
934
|
"agent.slug_taken": "conflict",
|
|
783
935
|
"agent.directory_conflict": "rule_violation",
|
|
784
936
|
"agent.repository_required": "rule_violation",
|
|
937
|
+
"agent.provider_token_required": "rule_violation",
|
|
938
|
+
"agent.provider_token_conflict": "rule_violation",
|
|
785
939
|
"agent.tool_duplicate": "rule_violation",
|
|
786
940
|
"agent.context_too_long": "rule_violation",
|
|
787
941
|
"agent.docker_unavailable": "rule_violation",
|
|
@@ -1124,7 +1278,8 @@ agentEventSchema.options.map((option) => option.shape.event.value);
|
|
|
1124
1278
|
provisioned: "platform.provisioned",
|
|
1125
1279
|
sandboxStarted: "platform.sandboxStarted",
|
|
1126
1280
|
sandboxStopped: "platform.sandboxStopped",
|
|
1127
|
-
restartedByNode: "platform.restartedByNode"
|
|
1281
|
+
restartedByNode: "platform.restartedByNode",
|
|
1282
|
+
claudeAccountChanged: "platform.claudeAccountChanged"
|
|
1128
1283
|
}).rawLog;
|
|
1129
1284
|
const agentStateSchema = z.enum([
|
|
1130
1285
|
"created",
|
|
@@ -2052,6 +2207,7 @@ const organizationSchema = z.object({
|
|
|
2052
2207
|
slug: organizationSlugSchema,
|
|
2053
2208
|
color: colorSchema,
|
|
2054
2209
|
logoHash: logoHashSchema.nullable(),
|
|
2210
|
+
claudeSwitchThreshold: switchThresholdSchema,
|
|
2055
2211
|
createdAt: z.iso.datetime()
|
|
2056
2212
|
}).meta({ id: "Organization" });
|
|
2057
2213
|
z.object({ items: z.array(organizationSchema) }).meta({ id: "OrganizationList" });
|
|
@@ -2112,7 +2268,8 @@ z.object({
|
|
|
2112
2268
|
const updateOrganizationBodySchema = z.object({
|
|
2113
2269
|
name: organizationNameSchema,
|
|
2114
2270
|
slug: organizationSlugSchema,
|
|
2115
|
-
color: colorSchema
|
|
2271
|
+
color: colorSchema,
|
|
2272
|
+
claudeSwitchThreshold: switchThresholdSchema.optional()
|
|
2116
2273
|
}).meta({ id: "UpdateOrganizationBody" });
|
|
2117
2274
|
z.object({ temporaryPassword: z.string() }).meta({ id: "TemporaryPassword" });
|
|
2118
2275
|
z.object({ items: z.array(userSchema) }).meta({ id: "UserList" });
|
|
@@ -2212,6 +2369,8 @@ const auditActionSchema = z.enum([
|
|
|
2212
2369
|
"credential.rotated",
|
|
2213
2370
|
"credential.deleted",
|
|
2214
2371
|
"credential.verified",
|
|
2372
|
+
"credential.identity_set",
|
|
2373
|
+
"project.claude_accounts_changed",
|
|
2215
2374
|
"repository.registered",
|
|
2216
2375
|
"repository.deleted",
|
|
2217
2376
|
"tool.created",
|
|
@@ -2389,14 +2548,21 @@ const repositoryStatusSchema = z.enum([
|
|
|
2389
2548
|
const repositoryNamePattern = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,98}[A-Za-z0-9])?$/;
|
|
2390
2549
|
const forbiddenBranchCharacters = /[\s\\~^:?*[\p{Cc}]/u;
|
|
2391
2550
|
const isSafeBranchName = (value) => value.length > 0 && value.length <= 255 && !value.startsWith("-") && !forbiddenBranchCharacters.test(value);
|
|
2392
|
-
const
|
|
2551
|
+
const isRepositoryOwner = (value) => {
|
|
2552
|
+
const segments = value.split("/");
|
|
2553
|
+
return value.length <= 255 && segments.length <= 20 && segments.every((segment) => repositoryNamePattern.test(segment));
|
|
2554
|
+
};
|
|
2555
|
+
const repositoryOwnerSchema = z.string().refine(isRepositoryOwner, { message: "expected one to twenty names separated by a slash" });
|
|
2393
2556
|
const repositoryNameSchema = z.string().regex(repositoryNamePattern);
|
|
2394
2557
|
const defaultBranchSchema = z.string().refine(isSafeBranchName, { message: "expected a branch name git and the TSV accept" });
|
|
2395
2558
|
const repositorySchema = z.object({
|
|
2396
2559
|
id: idSchema,
|
|
2397
2560
|
projectId: idSchema,
|
|
2398
2561
|
version: z.int().nonnegative(),
|
|
2399
|
-
githubId: z.int().positive(),
|
|
2562
|
+
githubId: z.int().positive().nullable(),
|
|
2563
|
+
provider: gitProviderSchema,
|
|
2564
|
+
serverUrl: z.string(),
|
|
2565
|
+
providerRepositoryId: z.string(),
|
|
2400
2566
|
owner: repositoryOwnerSchema,
|
|
2401
2567
|
name: repositoryNameSchema,
|
|
2402
2568
|
defaultBranch: defaultBranchSchema,
|
|
@@ -2418,16 +2584,18 @@ z.object({
|
|
|
2418
2584
|
id: idSchema,
|
|
2419
2585
|
repositoryId: idSchema
|
|
2420
2586
|
});
|
|
2421
|
-
const
|
|
2587
|
+
const availableRepositorySchema = z.object({
|
|
2422
2588
|
owner: repositoryOwnerSchema,
|
|
2423
2589
|
name: repositoryNameSchema,
|
|
2424
|
-
defaultBranch: defaultBranchSchema,
|
|
2590
|
+
defaultBranch: defaultBranchSchema.nullable(),
|
|
2425
2591
|
isPrivate: z.boolean()
|
|
2426
|
-
}).meta({ id: "
|
|
2592
|
+
}).meta({ id: "AvailableRepository" });
|
|
2427
2593
|
z.object({
|
|
2428
|
-
items: z.array(
|
|
2429
|
-
truncated: z.boolean()
|
|
2430
|
-
|
|
2594
|
+
items: z.array(availableRepositorySchema),
|
|
2595
|
+
truncated: z.boolean(),
|
|
2596
|
+
provider: gitProviderSchema,
|
|
2597
|
+
serverUrl: z.string()
|
|
2598
|
+
}).meta({ id: "AvailableRepositoryList" });
|
|
2431
2599
|
z.object({ credentialId: idSchema });
|
|
2432
2600
|
const toolKindSchema = z.enum([
|
|
2433
2601
|
"http_api",
|
|
@@ -2462,7 +2630,8 @@ const reservedEnvironmentPrefixes = [
|
|
|
2462
2630
|
"ANTHROPIC_",
|
|
2463
2631
|
"GH_",
|
|
2464
2632
|
"DOCKER_",
|
|
2465
|
-
"BC_"
|
|
2633
|
+
"BC_",
|
|
2634
|
+
"KC_GIT_"
|
|
2466
2635
|
];
|
|
2467
2636
|
const reservedEnvironmentNames = /* @__PURE__ */ new Set([
|
|
2468
2637
|
"PATH",
|
|
@@ -2742,7 +2911,9 @@ const agentRepositorySchema = z.object({
|
|
|
2742
2911
|
owner: repositoryOwnerSchema,
|
|
2743
2912
|
name: repositoryNameSchema,
|
|
2744
2913
|
defaultBranch: defaultBranchSchema,
|
|
2745
|
-
status: repositoryStatusSchema
|
|
2914
|
+
status: repositoryStatusSchema,
|
|
2915
|
+
provider: gitProviderSchema,
|
|
2916
|
+
serverUrl: z.string()
|
|
2746
2917
|
}).meta({ id: "AgentRepository" });
|
|
2747
2918
|
const agentRuntimeSchema = z.object({
|
|
2748
2919
|
available: z.boolean(),
|
|
@@ -2786,6 +2957,17 @@ const agentNodeSchema = z.object({
|
|
|
2786
2957
|
name: nodeNameSchema,
|
|
2787
2958
|
state: nodeStateSchema
|
|
2788
2959
|
}).meta({ id: "AgentNode" });
|
|
2960
|
+
const agentClaudeAccountSchema = z.object({
|
|
2961
|
+
current: z.object({
|
|
2962
|
+
id: idSchema,
|
|
2963
|
+
label: z.string(),
|
|
2964
|
+
kind: claudeCredentialKindSchema,
|
|
2965
|
+
position: z.int().positive().nullable()
|
|
2966
|
+
}).nullable(),
|
|
2967
|
+
queueSize: z.int().nonnegative(),
|
|
2968
|
+
waitReason: claudeAccountWaitReasonSchema.nullable(),
|
|
2969
|
+
waitUntil: z.iso.datetime().nullable()
|
|
2970
|
+
}).meta({ id: "AgentClaudeAccount" });
|
|
2789
2971
|
agentListItemSchema.extend({
|
|
2790
2972
|
version: z.int().nonnegative(),
|
|
2791
2973
|
description: z.string(),
|
|
@@ -2794,8 +2976,10 @@ agentListItemSchema.extend({
|
|
|
2794
2976
|
settings: kikimoraSettingsSchema,
|
|
2795
2977
|
effectiveSettings: workerSettingsSchema,
|
|
2796
2978
|
prompts: agentPromptsSchema,
|
|
2797
|
-
claudeCredential: credentialSummarySchema,
|
|
2798
|
-
|
|
2979
|
+
claudeCredential: credentialSummarySchema.nullable(),
|
|
2980
|
+
claudeAccount: agentClaudeAccountSchema,
|
|
2981
|
+
githubCredential: credentialSummarySchema.nullable(),
|
|
2982
|
+
gitCredentials: z.array(gitCredentialSummarySchema),
|
|
2799
2983
|
repositories: z.array(agentRepositorySchema),
|
|
2800
2984
|
toolAssignments: z.array(agentToolSchema),
|
|
2801
2985
|
release: releaseSummarySchema.nullable(),
|
|
@@ -2812,10 +2996,7 @@ const uniqueDirectories = {
|
|
|
2812
2996
|
message: "expected every repository to use its own directory",
|
|
2813
2997
|
path: ["repositories"]
|
|
2814
2998
|
};
|
|
2815
|
-
const
|
|
2816
|
-
message: "expected the Claude and the GitHub credential to be two credentials",
|
|
2817
|
-
path: ["githubCredentialId"]
|
|
2818
|
-
};
|
|
2999
|
+
const agentGitCredentialIdsSchema = z.array(idSchema).max(8).refine((ids) => new Set(ids).size === ids.length, { message: "expected every credential once" });
|
|
2819
3000
|
const hasUniqueTools = (assignments) => new Set(assignments.map((assignment) => assignment.toolId)).size === assignments.length;
|
|
2820
3001
|
const agentRepositoryLinksSchema = z.array(agentRepositoryLinkSchema).min(1).max(32);
|
|
2821
3002
|
const agentToolAssignmentsSchema = z.array(toolAssignmentSchema).max(32).refine(hasUniqueTools, { message: "expected every tool to be assigned once" });
|
|
@@ -2828,15 +3009,16 @@ const createAgentBodySchema = z.object({
|
|
|
2828
3009
|
kikimoraVersion: kikimoraVersionSchema,
|
|
2829
3010
|
nodeId: idSchema,
|
|
2830
3011
|
requiredCapabilities: requiredCapabilitiesSchema.optional(),
|
|
2831
|
-
claudeCredentialId: idSchema,
|
|
2832
|
-
githubCredentialId: idSchema,
|
|
3012
|
+
claudeCredentialId: idSchema.optional(),
|
|
3013
|
+
githubCredentialId: idSchema.optional(),
|
|
3014
|
+
gitCredentialIds: agentGitCredentialIdsSchema.default([]),
|
|
2833
3015
|
repositories: agentRepositoryLinksSchema,
|
|
2834
3016
|
toolAssignments: agentToolAssignmentsSchema,
|
|
2835
3017
|
prompts: agentPromptsSchema,
|
|
2836
3018
|
settings: kikimoraSettingsSchema,
|
|
2837
3019
|
timezone: timezoneSchema,
|
|
2838
3020
|
machine: agentMachineInputSchema
|
|
2839
|
-
}).refine((body) => hasUniqueDirectories(body.repositories), uniqueDirectories).
|
|
3021
|
+
}).refine((body) => hasUniqueDirectories(body.repositories), uniqueDirectories).meta({ id: "CreateAgentBody" });
|
|
2840
3022
|
const updateAgentSectionsSchema = z.object({
|
|
2841
3023
|
general: z.object({
|
|
2842
3024
|
name: agentNameSchema,
|
|
@@ -2845,9 +3027,10 @@ const updateAgentSectionsSchema = z.object({
|
|
|
2845
3027
|
}).optional(),
|
|
2846
3028
|
version: kikimoraVersionSchema.optional(),
|
|
2847
3029
|
credentials: z.object({
|
|
2848
|
-
claudeCredentialId: idSchema,
|
|
2849
|
-
githubCredentialId: idSchema
|
|
2850
|
-
|
|
3030
|
+
claudeCredentialId: idSchema.optional(),
|
|
3031
|
+
githubCredentialId: idSchema.optional(),
|
|
3032
|
+
gitCredentialIds: agentGitCredentialIdsSchema.default([])
|
|
3033
|
+
}).optional(),
|
|
2851
3034
|
repositories: agentRepositoryLinksSchema.optional(),
|
|
2852
3035
|
tools: agentToolAssignmentsSchema.optional(),
|
|
2853
3036
|
prompts: agentPromptsSchema.optional(),
|
|
@@ -3410,7 +3593,8 @@ const notificationKindSchema = z.enum([
|
|
|
3410
3593
|
"backup.overdue",
|
|
3411
3594
|
"host_operation.finished",
|
|
3412
3595
|
"dead_letter.queued",
|
|
3413
|
-
"node.resumed"
|
|
3596
|
+
"node.resumed",
|
|
3597
|
+
"project.claude_accounts_exhausted"
|
|
3414
3598
|
]);
|
|
3415
3599
|
const notificationSeveritySchema = z.enum([
|
|
3416
3600
|
"info",
|
|
@@ -3424,7 +3608,8 @@ const notificationTargetTypeSchema = z.enum([
|
|
|
3424
3608
|
"queue",
|
|
3425
3609
|
"volume",
|
|
3426
3610
|
"platform",
|
|
3427
|
-
"node"
|
|
3611
|
+
"node",
|
|
3612
|
+
"project"
|
|
3428
3613
|
]);
|
|
3429
3614
|
const hostOperationOutcomes = [
|
|
3430
3615
|
"succeeded",
|
|
@@ -3458,7 +3643,10 @@ z.discriminatedUnion("operation", [
|
|
|
3458
3643
|
role: volumeRoleSchema
|
|
3459
3644
|
})
|
|
3460
3645
|
]);
|
|
3461
|
-
z.strictObject({
|
|
3646
|
+
z.strictObject({
|
|
3647
|
+
label: textParam,
|
|
3648
|
+
kind: credentialKindSchema
|
|
3649
|
+
}), z.strictObject({
|
|
3462
3650
|
label: textParam,
|
|
3463
3651
|
expiresAt: instantParam
|
|
3464
3652
|
}), z.strictObject({
|
|
@@ -3491,7 +3679,10 @@ z.strictObject({ label: textParam }), z.strictObject({
|
|
|
3491
3679
|
}).refine((params) => params.pausedAgents === 0 === (params.pausedAgentNames === ""), {
|
|
3492
3680
|
message: "expected the names of the paused agents exactly when there are some",
|
|
3493
3681
|
path: ["pausedAgentNames"]
|
|
3494
|
-
})
|
|
3682
|
+
}), z.union([z.strictObject({
|
|
3683
|
+
blocked: z.literal(false),
|
|
3684
|
+
nearestResetAt: instantParam
|
|
3685
|
+
}), z.strictObject({ blocked: z.literal(true) })]);
|
|
3495
3686
|
const notificationParamsSchema = z.record(z.string(), z.union([
|
|
3496
3687
|
z.string(),
|
|
3497
3688
|
z.number(),
|
|
@@ -4434,6 +4625,22 @@ const runOf = async (adaptersOf, { settings, context }) => {
|
|
|
4434
4625
|
};
|
|
4435
4626
|
};
|
|
4436
4627
|
//#endregion
|
|
4628
|
+
//#region src/git-providers.ts
|
|
4629
|
+
const providerNames = {
|
|
4630
|
+
github: "GitHub",
|
|
4631
|
+
gitlab: "GitLab",
|
|
4632
|
+
bitbucket: "Bitbucket"
|
|
4633
|
+
};
|
|
4634
|
+
const isGitProvider = (value) => gitProviders.some((provider) => provider === value);
|
|
4635
|
+
const providerNameOf = (provider) => isGitProvider(provider) ? providerNames[provider] : "the provider";
|
|
4636
|
+
const gitProviderOf = (credential) => providerOfKind(credential.kind);
|
|
4637
|
+
const credentialServerOf = (credential) => {
|
|
4638
|
+
const provider = gitProviderOf(credential);
|
|
4639
|
+
if (provider === null) return null;
|
|
4640
|
+
return credential.metadata.serverUrl ?? defaultServerUrls[provider];
|
|
4641
|
+
};
|
|
4642
|
+
const kindOfProvider = (provider) => isGitProvider(provider) ? gitCredentialKindOf[provider] : "<kind>";
|
|
4643
|
+
//#endregion
|
|
4437
4644
|
//#region src/commands/agents/shared.ts
|
|
4438
4645
|
const agentOption = { project: { type: "string" } };
|
|
4439
4646
|
const agentUsage = [["--project <name>", "the project of the agent, when the agent is named alone"]];
|
|
@@ -4442,7 +4649,11 @@ const actionsAfterPause = /* @__PURE__ */ new Set([
|
|
|
4442
4649
|
"restart",
|
|
4443
4650
|
"resume"
|
|
4444
4651
|
]);
|
|
4652
|
+
const serverOf = (serverUrl) => typeof serverUrl === "string" ? serverUrl : "its server";
|
|
4445
4653
|
const agentHints = {
|
|
4654
|
+
"agent.provider_token_required": ({ provider, serverUrl }) => `The project needs a ${providerNameOf(provider)} token for ${serverOf(serverUrl)}: kcloud credentials add --project <name> --kind ${kindOfProvider(provider)}${provider === "github" ? "" : ` --server-url ${serverOf(serverUrl)}`} --secret-file <path> stores one, and gitCredentialIds names it.`,
|
|
4655
|
+
"agent.provider_token_conflict": ({ provider, serverUrl }) => `Two credentials of the agent are for ${providerNameOf(provider)} at ${serverOf(serverUrl)}; gitCredentialIds takes one for each server.`,
|
|
4656
|
+
"credential.kind_mismatch": "claudeCredentialId takes a Claude credential, and gitCredentialIds the github_pat, gitlab_token or bitbucket_token credentials of the project.",
|
|
4446
4657
|
"agent.not_found": "kcloud agents list --project <name> shows the agents.",
|
|
4447
4658
|
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
4448
4659
|
"agent.invalid_transition": ({ state, action }) => state === "pausing" && actionsAfterPause.has(String(action)) ? "The agent finishes its current session before it pauses; the action goes through once kcloud agents show <agent> says paused." : "kcloud agents show <agent> lists the actions it takes now.",
|
|
@@ -4450,6 +4661,7 @@ const agentHints = {
|
|
|
4450
4661
|
"concurrency.conflict": "Someone changed the agent meanwhile; read it again and retry.",
|
|
4451
4662
|
"agent.node_unsuitable": "The node lacks a capability the agent requires; kcloud nodes show <node> lists what it offers.",
|
|
4452
4663
|
"node.offline": "The node of the agent is not connected; kcloud nodes show <node> tells when it was last seen.",
|
|
4664
|
+
"account.queue_empty": "The queue of Claude accounts of the project is empty; kcloud projects accounts add <account> --project <name> puts one in it.",
|
|
4453
4665
|
"node.outdated": "The node of the agent runs an older release; kcloud nodes show <node> names the release it runs and its update."
|
|
4454
4666
|
};
|
|
4455
4667
|
const agentFrom = async (run, reference, project) => {
|
|
@@ -4802,8 +5014,7 @@ const agentPlanOf = (agent) => ({
|
|
|
4802
5014
|
kikimoraVersion: agent.kikimoraVersion,
|
|
4803
5015
|
...agent.node === null ? {} : { nodeId: agent.node.id },
|
|
4804
5016
|
requiredCapabilities: agent.requiredCapabilities,
|
|
4805
|
-
|
|
4806
|
-
githubCredentialId: agent.githubCredential.id,
|
|
5017
|
+
gitCredentialIds: agent.gitCredentials.map(({ id }) => id),
|
|
4807
5018
|
repositories: agent.repositories.map(({ repositoryId, directory }) => ({
|
|
4808
5019
|
repositoryId,
|
|
4809
5020
|
directory
|
|
@@ -4823,9 +5034,12 @@ const agentPlanOf = (agent) => ({
|
|
|
4823
5034
|
});
|
|
4824
5035
|
const machineLimitKeys = /* @__PURE__ */ new Set(["cpus", "memoryMb"]);
|
|
4825
5036
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4826
|
-
const
|
|
5037
|
+
const warned = ({ context }, text) => {
|
|
5038
|
+
context.terminal.error(context.terminal.errorStyles.warning(`warning ${text}`));
|
|
5039
|
+
};
|
|
5040
|
+
const warnIgnored = (run, keys) => {
|
|
4827
5041
|
const named = keys.map((key) => `machine.${key}`).join(" and ");
|
|
4828
|
-
|
|
5042
|
+
warned(run, `${named} ${keys.length === 1 ? "is" : "are"} ignored: an agent has no limits of its own, and the pools of its node limit its sessions.`);
|
|
4829
5043
|
};
|
|
4830
5044
|
const machineWithoutLimits = (run, machine) => {
|
|
4831
5045
|
if (!isRecord(machine)) return machine;
|
|
@@ -4834,17 +5048,40 @@ const machineWithoutLimits = (run, machine) => {
|
|
|
4834
5048
|
warnIgnored(run, ignored);
|
|
4835
5049
|
return Object.fromEntries(Object.entries(machine).filter(([key]) => !machineLimitKeys.has(key)));
|
|
4836
5050
|
};
|
|
4837
|
-
const
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
} :
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
5051
|
+
const claudeCredentialKey = "claudeCredentialId";
|
|
5052
|
+
const withoutClaudeCredential = (run, fields) => {
|
|
5053
|
+
if (!(claudeCredentialKey in fields)) return fields;
|
|
5054
|
+
warned(run, `${claudeCredentialKey} is ignored: an agent works on the queue of Claude accounts of its project, which kcloud projects accounts list shows.`);
|
|
5055
|
+
return Object.fromEntries(Object.entries(fields).filter(([key]) => key !== claudeCredentialKey));
|
|
5056
|
+
};
|
|
5057
|
+
const acceptedAgentFile = (run, document) => {
|
|
5058
|
+
if (!isRecord(document)) return document;
|
|
5059
|
+
const kept = withoutClaudeCredential(run, document);
|
|
5060
|
+
return "machine" in kept ? {
|
|
5061
|
+
...kept,
|
|
5062
|
+
machine: machineWithoutLimits(run, kept.machine)
|
|
5063
|
+
} : kept;
|
|
5064
|
+
};
|
|
5065
|
+
const credentialsSectionOf = (run, sections) => {
|
|
5066
|
+
if (!isRecord(sections.credentials)) return sections;
|
|
5067
|
+
const credentials = withoutClaudeCredential(run, sections.credentials);
|
|
5068
|
+
if (Object.keys(credentials).length > 0) return {
|
|
5069
|
+
...sections,
|
|
5070
|
+
credentials
|
|
5071
|
+
};
|
|
5072
|
+
return Object.fromEntries(Object.entries(sections).filter(([key]) => key !== "credentials"));
|
|
5073
|
+
};
|
|
5074
|
+
const acceptedAgentChange = (run, document) => {
|
|
5075
|
+
if (!isRecord(document) || !isRecord(document.sections)) return document;
|
|
5076
|
+
const sections = credentialsSectionOf(run, document.sections);
|
|
5077
|
+
return {
|
|
5078
|
+
...document,
|
|
5079
|
+
sections: "machine" in sections ? {
|
|
5080
|
+
...sections,
|
|
5081
|
+
machine: machineWithoutLimits(run, sections.machine)
|
|
5082
|
+
} : sections
|
|
5083
|
+
};
|
|
5084
|
+
};
|
|
4848
5085
|
//#endregion
|
|
4849
5086
|
//#region src/commands/agents/config.ts
|
|
4850
5087
|
const agentsOf = async (scope, project) => {
|
|
@@ -4960,7 +5197,7 @@ const configCommands = (adaptersOf) => [
|
|
|
4960
5197
|
const agentId = await agentFrom(run, invocation.positionals[0], invocation.values.project);
|
|
4961
5198
|
const file = invocation.values.file;
|
|
4962
5199
|
if (file === void 0) throw new UsageError("agents set needs --file <path>.", "The file holds the sections to change; kcloud agents show <agent> --json shows the current ones.");
|
|
4963
|
-
const agent = await changedAgent(run.api, agentId,
|
|
5200
|
+
const agent = await changedAgent(run.api, agentId, acceptedAgentChange(run, await documentOf(run, file)));
|
|
4964
5201
|
return printed(run, agent, (changed) => changed.restartRequired ? `Changed ${changed.name}; it needs ${changeTakerOf(changed)} to take the change.` : `Changed ${changed.name}.`);
|
|
4965
5202
|
}
|
|
4966
5203
|
}),
|
|
@@ -5013,13 +5250,13 @@ const waitTexts = {
|
|
|
5013
5250
|
};
|
|
5014
5251
|
const reasonOf$1 = ({ reason }) => reason ?? "slot";
|
|
5015
5252
|
const isKnownReason = (reason) => Object.hasOwn(waitTexts, reason);
|
|
5016
|
-
const waitTextOf = (reason) => isKnownReason(reason) ? waitTexts[reason] : `held by the node (${reason})`;
|
|
5253
|
+
const waitTextOf$1 = (reason) => isKnownReason(reason) ? waitTexts[reason] : `held by the node (${reason})`;
|
|
5017
5254
|
const waitRowsOf = ({ runtime }) => {
|
|
5018
5255
|
const { admission } = runtime;
|
|
5019
5256
|
if (admission === null) return [];
|
|
5020
5257
|
const waits = ["monitor", "executor"].flatMap((role) => {
|
|
5021
5258
|
const state = admission[role];
|
|
5022
|
-
return state?.state === "waiting" ? [`${role} ${waitTextOf(reasonOf$1(state))}`] : [];
|
|
5259
|
+
return state?.state === "waiting" ? [`${role} ${waitTextOf$1(reasonOf$1(state))}`] : [];
|
|
5023
5260
|
});
|
|
5024
5261
|
return waits.length === 0 ? [] : [["admissions", waits.join("; ")]];
|
|
5025
5262
|
};
|
|
@@ -5038,10 +5275,33 @@ const claudeCodeOf = ({ runtime, release }) => {
|
|
|
5038
5275
|
if (running?.source === "host") return "host";
|
|
5039
5276
|
return running?.version ?? release?.claudeCodeVersion ?? "unknown";
|
|
5040
5277
|
};
|
|
5278
|
+
const claudeAccountWaitOf = ({ waitReason, waitUntil }) => {
|
|
5279
|
+
switch (waitReason) {
|
|
5280
|
+
case "exhausted": return waitUntil === null ? "every account is exhausted" : `every account is exhausted until ${waitUntil}`;
|
|
5281
|
+
case "blocked": return "no account of the queue can work";
|
|
5282
|
+
case "empty": return "the queue of the project is empty";
|
|
5283
|
+
default: return null;
|
|
5284
|
+
}
|
|
5285
|
+
};
|
|
5286
|
+
const claudeAccountOf = ({ claudeAccount }) => {
|
|
5287
|
+
const { current, queueSize } = claudeAccount;
|
|
5288
|
+
const account = current === null ? "none" : `${current.label} (${current.kind}), ${current.position === null ? "outside the queue" : `${String(current.position)} of ${String(queueSize)}`}`;
|
|
5289
|
+
const wait = claudeAccountWaitOf(claudeAccount);
|
|
5290
|
+
return wait === null ? account : `${account}; ${wait}`;
|
|
5291
|
+
};
|
|
5041
5292
|
const failureRowsOf = ({ failureReason, retryAt }) => {
|
|
5042
5293
|
if (failureReason === null) return [];
|
|
5043
5294
|
return [["failure", retryAt === null ? failureReason : `${failureReason}, retry at ${retryAt}`]];
|
|
5044
5295
|
};
|
|
5296
|
+
const gitCredentialLinesOf = ({ gitCredentials }) => gitCredentials.length === 0 ? ["No git credential is set."] : ["GIT CREDENTIALS", renderTable([
|
|
5297
|
+
{ header: "PROVIDER" },
|
|
5298
|
+
{ header: "SERVER" },
|
|
5299
|
+
{ header: "LABEL" }
|
|
5300
|
+
], gitCredentials.map(({ provider, serverUrl, label }) => [
|
|
5301
|
+
provider,
|
|
5302
|
+
serverUrl,
|
|
5303
|
+
label
|
|
5304
|
+
]))];
|
|
5045
5305
|
const detailsOf = (agent) => [
|
|
5046
5306
|
renderTable([{ header: "FIELD" }, { header: "VALUE" }], [
|
|
5047
5307
|
["name", agent.name],
|
|
@@ -5051,6 +5311,7 @@ const detailsOf = (agent) => [
|
|
|
5051
5311
|
["observed", agent.observedState],
|
|
5052
5312
|
["release", agent.kikimoraVersion],
|
|
5053
5313
|
["claude code", claudeCodeOf(agent)],
|
|
5314
|
+
["claude account", claudeAccountOf(agent)],
|
|
5054
5315
|
["node", agent.node === null ? "none" : `${agent.node.name} (${agent.node.state})`],
|
|
5055
5316
|
["requires", agent.requiredCapabilities.length === 0 ? "nothing" : agent.requiredCapabilities.join(", ")],
|
|
5056
5317
|
["timezone", agent.timezone],
|
|
@@ -5071,13 +5332,19 @@ const detailsOf = (agent) => [
|
|
|
5071
5332
|
"",
|
|
5072
5333
|
agent.repositories.length === 0 ? "No repository is cloned into the workspace." : renderTable([
|
|
5073
5334
|
{ header: "REPOSITORY" },
|
|
5335
|
+
{ header: "PROVIDER" },
|
|
5336
|
+
{ header: "SERVER" },
|
|
5074
5337
|
{ header: "DIRECTORY" },
|
|
5075
5338
|
{ header: "STATUS" }
|
|
5076
5339
|
], agent.repositories.map((repository) => [
|
|
5077
5340
|
`${repository.owner}/${repository.name}`,
|
|
5341
|
+
repository.provider,
|
|
5342
|
+
repository.serverUrl,
|
|
5078
5343
|
repository.directory,
|
|
5079
5344
|
repository.status
|
|
5080
|
-
]))
|
|
5345
|
+
])),
|
|
5346
|
+
"",
|
|
5347
|
+
...gitCredentialLinesOf(agent)
|
|
5081
5348
|
].join("\n");
|
|
5082
5349
|
//#endregion
|
|
5083
5350
|
//#region src/commands/agents/placement.ts
|
|
@@ -5104,7 +5371,6 @@ const placedNodeId = async (run, { projectId, given, ask }) => {
|
|
|
5104
5371
|
};
|
|
5105
5372
|
//#endregion
|
|
5106
5373
|
//#region src/commands/agents/create.ts
|
|
5107
|
-
const claudeKinds = /* @__PURE__ */ new Set(["anthropic_api_key", "claude_oauth_token"]);
|
|
5108
5374
|
const catalogueOf = async (api, projectId) => {
|
|
5109
5375
|
const path = { params: { path: { id: projectId } } };
|
|
5110
5376
|
return {
|
|
@@ -5114,17 +5380,35 @@ const catalogueOf = async (api, projectId) => {
|
|
|
5114
5380
|
releases: dataOf("list the releases", await api.GET("/releases")).items.map(({ version }) => version)
|
|
5115
5381
|
};
|
|
5116
5382
|
};
|
|
5117
|
-
const
|
|
5118
|
-
const
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
}
|
|
5383
|
+
const serversOf = (catalogue, chosen) => {
|
|
5384
|
+
const servers = /* @__PURE__ */ new Map();
|
|
5385
|
+
for (const { repositoryId } of chosen) {
|
|
5386
|
+
const repository = catalogue.repositories.find(({ id }) => id === repositoryId);
|
|
5387
|
+
if (repository === void 0) continue;
|
|
5388
|
+
const { provider, serverUrl } = repository;
|
|
5389
|
+
servers.set(`${provider} ${serverUrl}`, {
|
|
5390
|
+
provider,
|
|
5391
|
+
serverUrl
|
|
5392
|
+
});
|
|
5393
|
+
}
|
|
5394
|
+
return [...servers.values()].sort((first, second) => gitProviders.indexOf(first.provider) - gitProviders.indexOf(second.provider) || first.serverUrl.localeCompare(second.serverUrl));
|
|
5395
|
+
};
|
|
5396
|
+
const gitCredentialQuestionOf = ({ provider, serverUrl }) => provider === "github" ? "The GitHub credential it clones with" : `The ${providerNameOf(provider)} credential for ${serverUrl} it clones with`;
|
|
5397
|
+
const chosenGitCredentials = async (prompt, catalogue, chosen) => {
|
|
5398
|
+
const ids = [];
|
|
5399
|
+
for (const server of serversOf(catalogue, chosen)) {
|
|
5400
|
+
const choices = catalogue.credentials.filter((credential) => gitProviderOf(credential) === server.provider && credentialServerOf(credential) === server.serverUrl).map((credential) => ({
|
|
5401
|
+
value: credential.id,
|
|
5402
|
+
label: credential.label,
|
|
5403
|
+
hint: credential.kind
|
|
5404
|
+
}));
|
|
5405
|
+
if (choices.length === 0) throw new CliError(`The project holds no ${providerNameOf(server.provider)} credential for ${server.serverUrl}.`, `kcloud credentials add --project <name> --kind ${kindOfProvider(server.provider)}${server.provider === "github" ? "" : ` --server-url ${server.serverUrl}`} stores one.`);
|
|
5406
|
+
ids.push(await prompt.select({
|
|
5407
|
+
message: gitCredentialQuestionOf(server),
|
|
5408
|
+
choices
|
|
5409
|
+
}));
|
|
5410
|
+
}
|
|
5411
|
+
return ids;
|
|
5128
5412
|
};
|
|
5129
5413
|
const chosenRepositories = async (prompt, catalogue) => {
|
|
5130
5414
|
if (catalogue.repositories.length === 0) throw new CliError("The project has no repository to clone.", "kcloud repositories add <owner/name> --project <name> registers one.");
|
|
@@ -5137,7 +5421,7 @@ const chosenRepositories = async (prompt, catalogue) => {
|
|
|
5137
5421
|
choices: [...left.map((repository) => ({
|
|
5138
5422
|
value: repository.id,
|
|
5139
5423
|
label: `${repository.owner}/${repository.name}`,
|
|
5140
|
-
hint: repository.defaultBranch
|
|
5424
|
+
hint: `${repository.provider} ${repository.defaultBranch}`
|
|
5141
5425
|
})), ...chosen.length === 0 ? [] : [{
|
|
5142
5426
|
value: "",
|
|
5143
5427
|
label: "no more repositories",
|
|
@@ -5232,15 +5516,16 @@ const askedPlan = async (run, projectId, values, required) => {
|
|
|
5232
5516
|
}))
|
|
5233
5517
|
});
|
|
5234
5518
|
if (!catalogue.releases.includes(release)) throw new CliError(`${release} is not a release an agent can run.`, catalogue.releases.length === 0 ? "The catalogue holds no release yet; an administrator syncs it under Releases in the panel." : `kcloud releases list shows ${catalogue.releases.join(", ")}.`);
|
|
5519
|
+
const repositories = await chosenRepositories(run.prompt, catalogue);
|
|
5520
|
+
const gitCredentialIds = await chosenGitCredentials(run.prompt, catalogue, repositories);
|
|
5235
5521
|
return {
|
|
5236
5522
|
name,
|
|
5237
5523
|
description: "",
|
|
5238
5524
|
kikimoraVersion: release,
|
|
5239
5525
|
nodeId,
|
|
5240
5526
|
...requirementOf(required),
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
repositories: [...await chosenRepositories(run.prompt, catalogue)],
|
|
5527
|
+
gitCredentialIds: [...gitCredentialIds],
|
|
5528
|
+
repositories: [...repositories],
|
|
5244
5529
|
toolAssignments: [...await chosenTools(run.prompt, catalogue)],
|
|
5245
5530
|
prompts: {
|
|
5246
5531
|
monitor: await promptText(run, values["monitor-prompt-file"], "What the monitor should watch for"),
|
|
@@ -5256,7 +5541,7 @@ const askedPlan = async (run, projectId, values, required) => {
|
|
|
5256
5541
|
};
|
|
5257
5542
|
const isObject = (document) => typeof document === "object" && document !== null && !Array.isArray(document);
|
|
5258
5543
|
const fileOf = async (run, projectId, { file, node }, required) => {
|
|
5259
|
-
const document =
|
|
5544
|
+
const document = acceptedAgentFile(run, await documentOf(run, file));
|
|
5260
5545
|
const placed = !isObject(document) || node === void 0 && "nodeId" in document ? document : {
|
|
5261
5546
|
...document,
|
|
5262
5547
|
nodeId: await placedNodeId(run, {
|
|
@@ -5289,6 +5574,7 @@ const createCommand = (adaptersOf) => defineCommand({
|
|
|
5289
5574
|
["--docker", "give the agent a Docker sidecar, when the project allows it"],
|
|
5290
5575
|
["--browser", "give the agent a browser"],
|
|
5291
5576
|
["", "kcloud agents show <agent> --as-file prints what --file takes"],
|
|
5577
|
+
["", "the file names the git tokens as gitCredentialIds, one for each server of its repositories"],
|
|
5292
5578
|
["", "--node and --require replace the nodeId and requiredCapabilities of the file"]
|
|
5293
5579
|
],
|
|
5294
5580
|
options: {
|
|
@@ -5498,7 +5784,7 @@ const workKinds = /* @__PURE__ */ new Set([
|
|
|
5498
5784
|
"summarising"
|
|
5499
5785
|
]);
|
|
5500
5786
|
const organizationDashboardOf = async (api, organizationId) => dataOf("show the dashboard of the organization", await api.GET("/organizations/{organizationId}/dashboard", { params: { path: { organizationId } } }), organizationHints);
|
|
5501
|
-
const statusOf = (agent) => agent.status === "transitioning" ? `${agent.status} (${agent.state})` : agent.status;
|
|
5787
|
+
const statusOf$1 = (agent) => agent.status === "transitioning" ? `${agent.status} (${agent.state})` : agent.status;
|
|
5502
5788
|
const taskOf = (agent) => {
|
|
5503
5789
|
const { activity } = agent;
|
|
5504
5790
|
if (activity === null) return noTask;
|
|
@@ -5511,7 +5797,7 @@ const dashboardText = (dashboard) => {
|
|
|
5511
5797
|
const rows = dashboard.projects.flatMap((project) => project.agents.map((agent) => [
|
|
5512
5798
|
project.slug,
|
|
5513
5799
|
agent.slug,
|
|
5514
|
-
statusOf(agent),
|
|
5800
|
+
statusOf$1(agent),
|
|
5515
5801
|
agent.since,
|
|
5516
5802
|
taskOf(agent),
|
|
5517
5803
|
agent.costTodayUsd.toFixed(2)
|
|
@@ -5544,7 +5830,7 @@ const frameLineOf = (names, frame, at) => {
|
|
|
5544
5830
|
slug: agent.slug
|
|
5545
5831
|
});
|
|
5546
5832
|
const project = names.projects.get(agent.projectId) ?? agent.projectId;
|
|
5547
|
-
return `${at.toISOString()} ${project}/${agent.slug} ${statusOf(agent)} ${taskOf(agent)}`;
|
|
5833
|
+
return `${at.toISOString()} ${project}/${agent.slug} ${statusOf$1(agent)} ${taskOf(agent)}`;
|
|
5548
5834
|
}
|
|
5549
5835
|
const removed = dashboardRemovalSchema.safeParse(frame.data);
|
|
5550
5836
|
if (frame.event === "dashboard.removed" && removed.success) {
|
|
@@ -5711,10 +5997,10 @@ const pageOptions = {
|
|
|
5711
5997
|
};
|
|
5712
5998
|
const allUsage = ["--all", "follow the cursor to the end instead of one page"];
|
|
5713
5999
|
const pageUsage = [["--limit <n>", `how many rows (default: 50)`], allUsage];
|
|
5714
|
-
const dayOf$
|
|
6000
|
+
const dayOf$2 = (date) => date.toISOString().slice(0, 10);
|
|
5715
6001
|
const costWindowOf = (days, now) => ({
|
|
5716
|
-
from: dayOf$
|
|
5717
|
-
to: dayOf$
|
|
6002
|
+
from: dayOf$2(/* @__PURE__ */ new Date(now.getTime() - (days - 1) * 24 * 60 * 6e4)),
|
|
6003
|
+
to: dayOf$2(now)
|
|
5718
6004
|
});
|
|
5719
6005
|
const windowFrom = (days, now) => {
|
|
5720
6006
|
const span = days === void 0 ? 7 : Number(days);
|
|
@@ -6438,16 +6724,57 @@ const secretUsage = [
|
|
|
6438
6724
|
];
|
|
6439
6725
|
//#endregion
|
|
6440
6726
|
//#region src/commands/credentials.ts
|
|
6441
|
-
const projectOption$
|
|
6442
|
-
const projectUsage$
|
|
6443
|
-
const usageUnavailable = "Only a Claude Code OAuth token reports the usage of a subscription.";
|
|
6727
|
+
const projectOption$3 = { project: { type: "string" } };
|
|
6728
|
+
const projectUsage$3 = [["--project <name>", "the project the credential belongs to, by slug, name or id"]];
|
|
6729
|
+
const usageUnavailable$1 = "Only a Claude Code OAuth token reports the usage of a subscription; kcloud claude-accounts usage shows it.";
|
|
6730
|
+
const defaultKind = "github_pat";
|
|
6731
|
+
const countOf = (value) => typeof value === "number" ? value : 0;
|
|
6732
|
+
const inUseHint = ({ agents, repositories }) => {
|
|
6733
|
+
return `Point the ${[...countOf(agents) > 0 || countOf(repositories) === 0 ? ["agents"] : [], ...countOf(repositories) > 0 ? ["repositories"] : []].join(" and the ")} at another credential first.`;
|
|
6734
|
+
};
|
|
6444
6735
|
const credentialHints = {
|
|
6445
6736
|
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
6446
6737
|
"credential.not_found": "kcloud credentials list --project <name> shows them.",
|
|
6447
6738
|
"credential.label_taken": "Another credential of the project already has that label.",
|
|
6448
|
-
"credential.in_use":
|
|
6449
|
-
"credential.usage_unavailable": usageUnavailable
|
|
6739
|
+
"credential.in_use": inUseHint,
|
|
6740
|
+
"credential.usage_unavailable": usageUnavailable$1,
|
|
6741
|
+
"credential.kind_mismatch": "A project holds GitHub, GitLab and Bitbucket tokens alone, and only they carry a commit identity; kcloud claude-accounts add stores a Claude subscription or an Anthropic API key in the organization."
|
|
6742
|
+
};
|
|
6743
|
+
const addHints = {
|
|
6744
|
+
...credentialHints,
|
|
6745
|
+
"credential.server_refused": "Give the https address of a public host, without credentials, query or fragment, as --server-url.",
|
|
6746
|
+
"validation.failed": "Check the options: --server-url takes an http or https address, and a Bitbucket Cloud API token (ATATT...) needs --account-email."
|
|
6747
|
+
};
|
|
6748
|
+
const gitOptionsUsage = [
|
|
6749
|
+
["--server-url <url>", "the server of a GitLab or Bitbucket token (default: gitlab.com or bitbucket.org)"],
|
|
6750
|
+
["--account-email <email>", "the Atlassian account of a Bitbucket Cloud API token"],
|
|
6751
|
+
["--commit-name <name>", "the name its commits carry, with --commit-email"],
|
|
6752
|
+
["--commit-email <email>", "the e-mail its commits carry, with --commit-name"]
|
|
6753
|
+
];
|
|
6754
|
+
const gitOptions = {
|
|
6755
|
+
"server-url": { type: "string" },
|
|
6756
|
+
"account-email": { type: "string" },
|
|
6757
|
+
"commit-name": { type: "string" },
|
|
6758
|
+
"commit-email": { type: "string" }
|
|
6759
|
+
};
|
|
6760
|
+
const gitFieldsOf = (kind, values) => {
|
|
6761
|
+
const given = Object.entries(values).filter(([name, value]) => name in gitOptions && value !== void 0).map(([name]) => `--${name}`);
|
|
6762
|
+
if (!isGitCredentialKind(kind)) {
|
|
6763
|
+
if (given.length === 0) return {};
|
|
6764
|
+
throw new UsageError(`${given.join(", ")} only suits a ${gitCredentialKinds.join(", ")} credential, not ${kind}.`);
|
|
6765
|
+
}
|
|
6766
|
+
if (kind === "github_pat" && values["server-url"] !== void 0) throw new UsageError("--server-url does not suit a github_pat credential, which always acts on github.com.");
|
|
6767
|
+
if (values["commit-name"] === void 0 !== (values["commit-email"] === void 0)) throw new UsageError("--commit-name and --commit-email go together.", "Pass both, or neither to take the identity the provider reports.");
|
|
6768
|
+
return {
|
|
6769
|
+
...values["server-url"] === void 0 ? {} : { serverUrl: values["server-url"] },
|
|
6770
|
+
...values["account-email"] === void 0 ? {} : { accountEmail: values["account-email"] },
|
|
6771
|
+
...values["commit-name"] === void 0 ? {} : { commitName: values["commit-name"] },
|
|
6772
|
+
...values["commit-email"] === void 0 ? {} : { commitEmail: values["commit-email"] }
|
|
6773
|
+
};
|
|
6450
6774
|
};
|
|
6775
|
+
const missingIdentityNotes = (credentials, project) => credentials.filter(({ kind, verification, metadata }) => (kind === "gitlab_token" || kind === "bitbucket_token") && verification === "valid" && (metadata.commitName === void 0 || metadata.commitEmail === void 0)).map(({ label }) => `${label} has no commit identity yet: kcloud credentials set-identity ${shellWord(label)} --project ${project === void 0 ? "<name>" : shellWord(project)} --commit-name <name> --commit-email <email>`);
|
|
6776
|
+
const plainShellWord = /^[A-Za-z0-9_./@:-]+$/;
|
|
6777
|
+
const shellWord = (value) => plainShellWord.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
|
|
6451
6778
|
const percent = 100;
|
|
6452
6779
|
const notApplicable = "-";
|
|
6453
6780
|
const usedOf = (window) => window === null ? notApplicable : `${String(Math.round(window.utilization * percent))}%`;
|
|
@@ -6474,12 +6801,13 @@ const usageReportOf = (credential) => {
|
|
|
6474
6801
|
window?.resetsAt ?? notApplicable
|
|
6475
6802
|
]))}\n${usage.stale ? `Read at ${usage.readAt}; the latest reading failed.` : `Read at ${usage.readAt}.`}`;
|
|
6476
6803
|
};
|
|
6804
|
+
const accountOf = ({ metadata }) => metadata.accountName ?? metadata.githubLogin ?? notApplicable;
|
|
6477
6805
|
const isoDateLength = 10;
|
|
6478
6806
|
const expiryOf = ({ expiresAt, expiresAtComputed }) => {
|
|
6479
6807
|
if (expiresAt === null) return "never";
|
|
6480
6808
|
return expiresAtComputed ? `about ${expiresAt.slice(0, isoDateLength)}` : expiresAt;
|
|
6481
6809
|
};
|
|
6482
|
-
const projectFrom$
|
|
6810
|
+
const projectFrom$3 = async (run, given) => {
|
|
6483
6811
|
if (given === void 0) throw new UsageError("This command needs a project.", "Pass --project <name>; kcloud projects list shows them.");
|
|
6484
6812
|
return resolveProjectId(run, given);
|
|
6485
6813
|
};
|
|
@@ -6495,55 +6823,60 @@ const resolveCredential = async (api, projectId, reference) => {
|
|
|
6495
6823
|
const resolveCredentialId = async (api, projectId, reference) => (await resolveCredential(api, projectId, reference)).id;
|
|
6496
6824
|
const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
6497
6825
|
name: "credentials",
|
|
6498
|
-
summary: "The
|
|
6826
|
+
summary: "The GitHub, GitLab and Bitbucket tokens of a project.",
|
|
6499
6827
|
usage: "credentials <command>",
|
|
6500
6828
|
subcommands: [
|
|
6501
6829
|
defineCommand({
|
|
6502
6830
|
name: "list",
|
|
6503
6831
|
summary: "List the credentials of a project.",
|
|
6504
6832
|
usage: "credentials list --project <name>",
|
|
6505
|
-
optionsUsage: projectUsage$
|
|
6506
|
-
options: projectOption$
|
|
6833
|
+
optionsUsage: projectUsage$3,
|
|
6834
|
+
options: projectOption$3,
|
|
6507
6835
|
positionals: {
|
|
6508
6836
|
min: 0,
|
|
6509
6837
|
max: 0
|
|
6510
6838
|
},
|
|
6511
6839
|
run: async (invocation) => {
|
|
6512
6840
|
const run = await runOf(adaptersOf, invocation);
|
|
6513
|
-
const projectId = await projectFrom$
|
|
6841
|
+
const projectId = await projectFrom$3(run, invocation.values.project);
|
|
6514
6842
|
const items = await credentialsOf(run.api, projectId);
|
|
6515
|
-
return printed(run, items, (credentials) => credentials.length === 0 ? "The project holds no credential yet." : renderTable([
|
|
6843
|
+
return printed(run, items, (credentials) => credentials.length === 0 ? "The project holds no credential yet." : [renderTable([
|
|
6516
6844
|
{ header: "LABEL" },
|
|
6517
6845
|
{ header: "KIND" },
|
|
6846
|
+
{ header: "SERVER" },
|
|
6847
|
+
{ header: "ACCOUNT" },
|
|
6518
6848
|
{ header: "FINGERPRINT" },
|
|
6519
6849
|
{ header: "VERIFIED" },
|
|
6520
6850
|
{ header: "EXPIRES" },
|
|
6521
|
-
{ header: "USAGE" },
|
|
6522
6851
|
{ header: "ID" }
|
|
6523
6852
|
], credentials.map((credential) => [
|
|
6524
6853
|
credential.label,
|
|
6525
6854
|
credential.kind,
|
|
6855
|
+
credentialServerOf(credential) ?? notApplicable,
|
|
6856
|
+
accountOf(credential),
|
|
6526
6857
|
credential.fingerprint,
|
|
6527
6858
|
credential.verification,
|
|
6528
6859
|
expiryOf(credential),
|
|
6529
|
-
usageSummaryOf(credential),
|
|
6530
6860
|
credential.id
|
|
6531
|
-
])));
|
|
6861
|
+
])), ...missingIdentityNotes(credentials, invocation.values.project).flatMap((note, index) => index === 0 ? ["", note] : [note])].join("\n"));
|
|
6532
6862
|
}
|
|
6533
6863
|
}),
|
|
6534
6864
|
defineCommand({
|
|
6535
6865
|
name: "add",
|
|
6536
6866
|
summary: "Store a credential in a project, asking for the secret.",
|
|
6537
|
-
usage: "credentials add --project <name> --kind <kind> --label <label> [--secret-file <path>]",
|
|
6867
|
+
usage: "credentials add --project <name> [--kind <kind>] --label <label> [--server-url <url>] [--secret-file <path>]",
|
|
6538
6868
|
optionsUsage: [
|
|
6539
|
-
...projectUsage$
|
|
6540
|
-
["--kind <kind>", `
|
|
6869
|
+
...projectUsage$3,
|
|
6870
|
+
["--kind <kind>", `the kind of the credential (default: ${defaultKind})`],
|
|
6871
|
+
["", "kcloud claude-accounts add stores a Claude account in the organization"],
|
|
6541
6872
|
["--label <label>", "how the panel and the agents name it"],
|
|
6873
|
+
...gitOptionsUsage,
|
|
6542
6874
|
...secretUsage
|
|
6543
6875
|
],
|
|
6544
6876
|
options: {
|
|
6545
|
-
...projectOption$
|
|
6877
|
+
...projectOption$3,
|
|
6546
6878
|
...secretOptions,
|
|
6879
|
+
...gitOptions,
|
|
6547
6880
|
kind: { type: "string" },
|
|
6548
6881
|
label: { type: "string" }
|
|
6549
6882
|
},
|
|
@@ -6554,11 +6887,13 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6554
6887
|
run: async (invocation) => {
|
|
6555
6888
|
const { values } = invocation;
|
|
6556
6889
|
const run = await runOf(adaptersOf, invocation);
|
|
6557
|
-
const projectId = await projectFrom$
|
|
6558
|
-
const
|
|
6559
|
-
|
|
6890
|
+
const projectId = await projectFrom$3(run, values.project);
|
|
6891
|
+
const given = values.kind ?? defaultKind;
|
|
6892
|
+
const kind = credentialKinds.find((candidate) => candidate === given);
|
|
6893
|
+
if (kind === void 0) throw new UsageError(`--kind ${given} is not a kind of credential.`, `The kinds are ${credentialKinds.join(", ")}.`);
|
|
6560
6894
|
const label = values.label ?? "";
|
|
6561
6895
|
if (label.trim() === "") throw new UsageError("credentials add needs --label <label>.");
|
|
6896
|
+
const gitFields = gitFieldsOf(kind, values);
|
|
6562
6897
|
const secret = await secretOf({
|
|
6563
6898
|
run,
|
|
6564
6899
|
file: values["secret-file"],
|
|
@@ -6570,19 +6905,60 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6570
6905
|
body: {
|
|
6571
6906
|
kind,
|
|
6572
6907
|
label,
|
|
6573
|
-
secret
|
|
6908
|
+
secret,
|
|
6909
|
+
...gitFields
|
|
6574
6910
|
}
|
|
6575
|
-
}),
|
|
6911
|
+
}), addHints);
|
|
6576
6912
|
return printed(run, credential, (stored) => `Stored ${stored.label} (${stored.fingerprint}) as ${stored.id}.`);
|
|
6577
6913
|
}
|
|
6578
6914
|
}),
|
|
6915
|
+
defineCommand({
|
|
6916
|
+
name: "set-identity",
|
|
6917
|
+
summary: "Set the name and e-mail the commits of a git token carry.",
|
|
6918
|
+
usage: "credentials set-identity <credential> --project <name> --commit-name <name> --commit-email <email>",
|
|
6919
|
+
optionsUsage: [
|
|
6920
|
+
...projectUsage$3,
|
|
6921
|
+
["--commit-name <name>", "the name its commits carry"],
|
|
6922
|
+
["--commit-email <email>", "the e-mail its commits carry"],
|
|
6923
|
+
["", "the agents that use the token need a restart to sign with it"]
|
|
6924
|
+
],
|
|
6925
|
+
options: {
|
|
6926
|
+
...projectOption$3,
|
|
6927
|
+
"commit-name": { type: "string" },
|
|
6928
|
+
"commit-email": { type: "string" }
|
|
6929
|
+
},
|
|
6930
|
+
positionals: {
|
|
6931
|
+
min: 1,
|
|
6932
|
+
max: 1
|
|
6933
|
+
},
|
|
6934
|
+
run: async (invocation) => {
|
|
6935
|
+
const { values } = invocation;
|
|
6936
|
+
const commitName = values["commit-name"] ?? "";
|
|
6937
|
+
const commitEmail = values["commit-email"] ?? "";
|
|
6938
|
+
if (commitName.trim() === "" || commitEmail.trim() === "") throw new UsageError("credentials set-identity needs --commit-name <name> and --commit-email <email>.");
|
|
6939
|
+
const run = await runOf(adaptersOf, invocation);
|
|
6940
|
+
const projectId = await projectFrom$3(run, values.project);
|
|
6941
|
+
const credentialId = await resolveCredentialId(run.api, projectId, invocation.positionals[0] ?? "");
|
|
6942
|
+
const credential = dataOf("set the commit identity of the credential", await run.api.PUT("/projects/{id}/credentials/{credentialId}/commit-identity", {
|
|
6943
|
+
params: { path: {
|
|
6944
|
+
id: projectId,
|
|
6945
|
+
credentialId
|
|
6946
|
+
} },
|
|
6947
|
+
body: {
|
|
6948
|
+
commitName,
|
|
6949
|
+
commitEmail
|
|
6950
|
+
}
|
|
6951
|
+
}), credentialHints);
|
|
6952
|
+
return printed(run, credential, (changed) => `${changed.label} signs commits as ${commitName} <${commitEmail}> now.`);
|
|
6953
|
+
}
|
|
6954
|
+
}),
|
|
6579
6955
|
defineCommand({
|
|
6580
6956
|
name: "rename",
|
|
6581
6957
|
summary: "Change the label of a credential.",
|
|
6582
6958
|
usage: "credentials rename <credential> --project <name> --label <label>",
|
|
6583
|
-
optionsUsage: [...projectUsage$
|
|
6959
|
+
optionsUsage: [...projectUsage$3, ["--label <label>", "the new label"]],
|
|
6584
6960
|
options: {
|
|
6585
|
-
...projectOption$
|
|
6961
|
+
...projectOption$3,
|
|
6586
6962
|
label: { type: "string" }
|
|
6587
6963
|
},
|
|
6588
6964
|
positionals: {
|
|
@@ -6592,7 +6968,7 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6592
6968
|
run: async (invocation) => {
|
|
6593
6969
|
const { values } = invocation;
|
|
6594
6970
|
const run = await runOf(adaptersOf, invocation);
|
|
6595
|
-
const projectId = await projectFrom$
|
|
6971
|
+
const projectId = await projectFrom$3(run, values.project);
|
|
6596
6972
|
const credentialId = await resolveCredentialId(run.api, projectId, invocation.positionals[0] ?? "");
|
|
6597
6973
|
const label = values.label ?? "";
|
|
6598
6974
|
if (label.trim() === "") throw new UsageError("credentials rename needs --label <label>.");
|
|
@@ -6611,12 +6987,12 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6611
6987
|
summary: "Replace the secret of a credential.",
|
|
6612
6988
|
usage: "credentials rotate <credential> --project <name> [--restart-agents] [--secret-file <path>]",
|
|
6613
6989
|
optionsUsage: [
|
|
6614
|
-
...projectUsage$
|
|
6990
|
+
...projectUsage$3,
|
|
6615
6991
|
["--restart-agents", "restart the agents that use it once the secret is in"],
|
|
6616
6992
|
...secretUsage
|
|
6617
6993
|
],
|
|
6618
6994
|
options: {
|
|
6619
|
-
...projectOption$
|
|
6995
|
+
...projectOption$3,
|
|
6620
6996
|
...secretOptions,
|
|
6621
6997
|
"restart-agents": {
|
|
6622
6998
|
type: "boolean",
|
|
@@ -6630,7 +7006,7 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6630
7006
|
run: async (invocation) => {
|
|
6631
7007
|
const { values } = invocation;
|
|
6632
7008
|
const run = await runOf(adaptersOf, invocation);
|
|
6633
|
-
const projectId = await projectFrom$
|
|
7009
|
+
const projectId = await projectFrom$3(run, values.project);
|
|
6634
7010
|
const credentialId = await resolveCredentialId(run.api, projectId, invocation.positionals[0] ?? "");
|
|
6635
7011
|
const secret = await secretOf({
|
|
6636
7012
|
run,
|
|
@@ -6655,15 +7031,15 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6655
7031
|
name: "verify",
|
|
6656
7032
|
summary: "Ask the instance to verify a credential against its service.",
|
|
6657
7033
|
usage: "credentials verify <credential> --project <name>",
|
|
6658
|
-
optionsUsage: projectUsage$
|
|
6659
|
-
options: projectOption$
|
|
7034
|
+
optionsUsage: projectUsage$3,
|
|
7035
|
+
options: projectOption$3,
|
|
6660
7036
|
positionals: {
|
|
6661
7037
|
min: 1,
|
|
6662
7038
|
max: 1
|
|
6663
7039
|
},
|
|
6664
7040
|
run: async (invocation) => {
|
|
6665
7041
|
const run = await runOf(adaptersOf, invocation);
|
|
6666
|
-
const projectId = await projectFrom$
|
|
7042
|
+
const projectId = await projectFrom$3(run, invocation.values.project);
|
|
6667
7043
|
const credentialId = await resolveCredentialId(run.api, projectId, invocation.positionals[0] ?? "");
|
|
6668
7044
|
doneWith("verify the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/verify", { params: { path: {
|
|
6669
7045
|
id: projectId,
|
|
@@ -6676,9 +7052,9 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6676
7052
|
name: "usage",
|
|
6677
7053
|
summary: "Show the usage of the Claude subscription behind a Claude Code OAuth token.",
|
|
6678
7054
|
usage: "credentials usage <credential> --project <name> [--refresh]",
|
|
6679
|
-
optionsUsage: [...projectUsage$
|
|
7055
|
+
optionsUsage: [...projectUsage$3, ["--refresh", "read it from Anthropic now; within 30 seconds the last reading"]],
|
|
6680
7056
|
options: {
|
|
6681
|
-
...projectOption$
|
|
7057
|
+
...projectOption$3,
|
|
6682
7058
|
refresh: {
|
|
6683
7059
|
type: "boolean",
|
|
6684
7060
|
default: false
|
|
@@ -6690,9 +7066,9 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6690
7066
|
},
|
|
6691
7067
|
run: async (invocation) => {
|
|
6692
7068
|
const run = await runOf(adaptersOf, invocation);
|
|
6693
|
-
const projectId = await projectFrom$
|
|
7069
|
+
const projectId = await projectFrom$3(run, invocation.values.project);
|
|
6694
7070
|
const stored = await resolveCredential(run.api, projectId, invocation.positionals[0] ?? "");
|
|
6695
|
-
if (stored.kind !== "claude_oauth_token") throw new CliError(`${stored.label} is a ${stored.kind}, which reports no usage.`, usageUnavailable);
|
|
7071
|
+
if (stored.kind !== "claude_oauth_token") throw new CliError(`${stored.label} is a ${stored.kind}, which reports no usage.`, usageUnavailable$1);
|
|
6696
7072
|
const credential = invocation.values.refresh ? dataOf("read the usage of the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/usage", { params: { path: {
|
|
6697
7073
|
id: projectId,
|
|
6698
7074
|
credentialId: stored.id
|
|
@@ -6704,15 +7080,15 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6704
7080
|
name: "delete",
|
|
6705
7081
|
summary: "Delete a credential no agent uses.",
|
|
6706
7082
|
usage: "credentials delete <credential> --project <name>",
|
|
6707
|
-
optionsUsage: projectUsage$
|
|
6708
|
-
options: projectOption$
|
|
7083
|
+
optionsUsage: projectUsage$3,
|
|
7084
|
+
options: projectOption$3,
|
|
6709
7085
|
positionals: {
|
|
6710
7086
|
min: 1,
|
|
6711
7087
|
max: 1
|
|
6712
7088
|
},
|
|
6713
7089
|
run: async (invocation) => {
|
|
6714
7090
|
const run = await runOf(adaptersOf, invocation);
|
|
6715
|
-
const projectId = await projectFrom$
|
|
7091
|
+
const projectId = await projectFrom$3(run, invocation.values.project);
|
|
6716
7092
|
const reference = invocation.positionals[0] ?? "";
|
|
6717
7093
|
const credentialId = await resolveCredentialId(run.api, projectId, reference);
|
|
6718
7094
|
await confirmed({
|
|
@@ -6730,6 +7106,263 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6730
7106
|
]
|
|
6731
7107
|
});
|
|
6732
7108
|
//#endregion
|
|
7109
|
+
//#region src/commands/claude-accounts.ts
|
|
7110
|
+
const accountUsage$1 = [["", "an account is named by its label or its id"]];
|
|
7111
|
+
const usageUnavailable = "Only a Claude subscription reports its usage.";
|
|
7112
|
+
const queuesText = (projects) => typeof projects === "string" && projects !== "" ? projects : "some projects";
|
|
7113
|
+
const claudeAccountHints = {
|
|
7114
|
+
...organizationHints,
|
|
7115
|
+
"auth.forbidden": "Changing a Claude account takes an admin of the organization.",
|
|
7116
|
+
"account.not_found": "kcloud claude-accounts list shows the Claude accounts of the organization.",
|
|
7117
|
+
"account.label_taken": "Another Claude account of the organization already has that label.",
|
|
7118
|
+
"account.in_queue": ({ projects }) => `It stands in the queue of ${queuesText(projects)}; kcloud projects accounts remove <account> --project <project> takes it out.`,
|
|
7119
|
+
"credential.usage_unavailable": usageUnavailable
|
|
7120
|
+
};
|
|
7121
|
+
const queuesOf = (queues) => queues.length === 0 ? "-" : queues.map(({ projectName, position }) => `${projectName} #${String(position)}`).join(", ");
|
|
7122
|
+
const statusOf = ({ verification, creditsExhaustedAt }) => creditsExhaustedAt === null ? verification : "no credits";
|
|
7123
|
+
const claudeAccountsOf = async (api, organizationId) => dataOf("list the Claude accounts of the organization", await api.GET("/organizations/{organizationId}/claude-accounts", { params: { path: { organizationId } } }), claudeAccountHints).items;
|
|
7124
|
+
const claudeAccountIn = (accounts, reference) => {
|
|
7125
|
+
const found = accounts.filter((account) => account.id === reference.toLowerCase() || account.label === reference);
|
|
7126
|
+
const [first] = found;
|
|
7127
|
+
if (first === void 0) throw new CliError(`The organization has no Claude account ${reference}.`, accounts.length === 0 ? "kcloud claude-accounts add stores one." : `Its Claude accounts are ${accounts.map(({ label }) => label).join(", ")}.`);
|
|
7128
|
+
if (found.length > 1) throw new CliError(`${reference} names ${found.length} Claude accounts.`, "Name it by its id instead.");
|
|
7129
|
+
return first;
|
|
7130
|
+
};
|
|
7131
|
+
const resolveClaudeAccount = async (scope, reference) => {
|
|
7132
|
+
const { id } = await scope.organization();
|
|
7133
|
+
return {
|
|
7134
|
+
organizationId: id,
|
|
7135
|
+
account: claudeAccountIn(await claudeAccountsOf(scope.api, id), reference)
|
|
7136
|
+
};
|
|
7137
|
+
};
|
|
7138
|
+
const renamedClaudeAccount = async (api, organizationId, credentialId, label) => dataOf("rename the Claude account", await api.PATCH("/organizations/{organizationId}/claude-accounts/{credentialId}", {
|
|
7139
|
+
params: { path: {
|
|
7140
|
+
organizationId,
|
|
7141
|
+
credentialId
|
|
7142
|
+
} },
|
|
7143
|
+
body: { label }
|
|
7144
|
+
}), claudeAccountHints);
|
|
7145
|
+
const verifyClaudeAccount = async (api, organizationId, credentialId) => {
|
|
7146
|
+
doneWith("verify the Claude account", await api.POST("/organizations/{organizationId}/claude-accounts/{credentialId}/verify", { params: { path: {
|
|
7147
|
+
organizationId,
|
|
7148
|
+
credentialId
|
|
7149
|
+
} } }), claudeAccountHints);
|
|
7150
|
+
};
|
|
7151
|
+
const refreshedClaudeAccount = async (api, organizationId, credentialId) => dataOf("read the usage of the Claude account", await api.POST("/organizations/{organizationId}/claude-accounts/{credentialId}/usage", { params: { path: {
|
|
7152
|
+
organizationId,
|
|
7153
|
+
credentialId
|
|
7154
|
+
} } }), claudeAccountHints);
|
|
7155
|
+
const deleteClaudeAccount = async (api, organizationId, credentialId) => {
|
|
7156
|
+
doneWith("delete the Claude account", await api.DELETE("/organizations/{organizationId}/claude-accounts/{credentialId}", { params: { path: {
|
|
7157
|
+
organizationId,
|
|
7158
|
+
credentialId
|
|
7159
|
+
} } }), claudeAccountHints);
|
|
7160
|
+
};
|
|
7161
|
+
const createClaudeAccountsCommand = (adaptersOf) => defineGroup({
|
|
7162
|
+
name: "claude-accounts",
|
|
7163
|
+
summary: "The Claude subscriptions and Anthropic API keys of the organization.",
|
|
7164
|
+
usage: "claude-accounts <command>",
|
|
7165
|
+
subcommands: [
|
|
7166
|
+
defineCommand({
|
|
7167
|
+
name: "list",
|
|
7168
|
+
summary: "List the Claude accounts of the organization and the queues they are in.",
|
|
7169
|
+
usage: "claude-accounts list",
|
|
7170
|
+
options: {},
|
|
7171
|
+
positionals: {
|
|
7172
|
+
min: 0,
|
|
7173
|
+
max: 0
|
|
7174
|
+
},
|
|
7175
|
+
run: async (invocation) => {
|
|
7176
|
+
const run = await runOf(adaptersOf, invocation);
|
|
7177
|
+
const organization = await run.organization();
|
|
7178
|
+
const items = await claudeAccountsOf(run.api, organization.id);
|
|
7179
|
+
return printed(run, items, (accounts) => accounts.length === 0 ? `${organization.slug} has no Claude account yet; kcloud claude-accounts add stores one.` : renderTable([
|
|
7180
|
+
{ header: "KIND" },
|
|
7181
|
+
{ header: "LABEL" },
|
|
7182
|
+
{ header: "STATUS" },
|
|
7183
|
+
{ header: "USAGE" },
|
|
7184
|
+
{ header: "QUEUES" },
|
|
7185
|
+
{ header: "ID" }
|
|
7186
|
+
], accounts.map((account) => [
|
|
7187
|
+
account.kind,
|
|
7188
|
+
account.label,
|
|
7189
|
+
statusOf(account),
|
|
7190
|
+
usageSummaryOf(account),
|
|
7191
|
+
queuesOf(account.queues),
|
|
7192
|
+
account.id
|
|
7193
|
+
])));
|
|
7194
|
+
}
|
|
7195
|
+
}),
|
|
7196
|
+
defineCommand({
|
|
7197
|
+
name: "add",
|
|
7198
|
+
summary: "Store a Claude subscription or an Anthropic API key, asking for it.",
|
|
7199
|
+
usage: "claude-accounts add --kind <kind> --label <label> [--secret-file <path>]",
|
|
7200
|
+
optionsUsage: [
|
|
7201
|
+
["--kind <kind>", "claude_oauth_token for a subscription, anthropic_api_key for an API key"],
|
|
7202
|
+
["--label <label>", "how the panel and the queues name it"],
|
|
7203
|
+
...secretUsage,
|
|
7204
|
+
["", "kcloud projects accounts add puts it in the queue of a project"]
|
|
7205
|
+
],
|
|
7206
|
+
options: {
|
|
7207
|
+
...secretOptions,
|
|
7208
|
+
kind: { type: "string" },
|
|
7209
|
+
label: { type: "string" }
|
|
7210
|
+
},
|
|
7211
|
+
positionals: {
|
|
7212
|
+
min: 0,
|
|
7213
|
+
max: 0
|
|
7214
|
+
},
|
|
7215
|
+
run: async (invocation) => {
|
|
7216
|
+
const { values } = invocation;
|
|
7217
|
+
const kind = claudeCredentialKinds.find((candidate) => candidate === values.kind);
|
|
7218
|
+
if (kind === void 0) throw new UsageError(`--kind ${values.kind ?? ""} is not a kind of Claude account.`, `The kinds are ${claudeCredentialKinds.join(", ")}.`);
|
|
7219
|
+
const label = values.label ?? "";
|
|
7220
|
+
if (label.trim() === "") throw new UsageError("claude-accounts add needs --label <label>.");
|
|
7221
|
+
const run = await runOf(adaptersOf, invocation);
|
|
7222
|
+
const organization = await run.organization();
|
|
7223
|
+
const secret = await secretOf({
|
|
7224
|
+
run,
|
|
7225
|
+
file: values["secret-file"],
|
|
7226
|
+
fromStdin: values["secret-stdin"],
|
|
7227
|
+
question: `The secret of ${label}`
|
|
7228
|
+
});
|
|
7229
|
+
const account = dataOf("store the Claude account", await run.api.POST("/organizations/{organizationId}/claude-accounts", {
|
|
7230
|
+
params: { path: { organizationId: organization.id } },
|
|
7231
|
+
body: {
|
|
7232
|
+
kind,
|
|
7233
|
+
label,
|
|
7234
|
+
secret
|
|
7235
|
+
}
|
|
7236
|
+
}), claudeAccountHints);
|
|
7237
|
+
return printed(run, account, (stored) => `Stored ${stored.label} (${stored.fingerprint}) as ${stored.id}; the instance is verifying it.`);
|
|
7238
|
+
}
|
|
7239
|
+
}),
|
|
7240
|
+
defineCommand({
|
|
7241
|
+
name: "rename",
|
|
7242
|
+
summary: "Change the label of a Claude account.",
|
|
7243
|
+
usage: "claude-accounts rename <account> --label <label>",
|
|
7244
|
+
optionsUsage: [...accountUsage$1, ["--label <label>", "the new label"]],
|
|
7245
|
+
options: { label: { type: "string" } },
|
|
7246
|
+
positionals: {
|
|
7247
|
+
min: 1,
|
|
7248
|
+
max: 1
|
|
7249
|
+
},
|
|
7250
|
+
run: async (invocation) => {
|
|
7251
|
+
const label = invocation.values.label ?? "";
|
|
7252
|
+
if (label.trim() === "") throw new UsageError("claude-accounts rename needs --label <label>.");
|
|
7253
|
+
const run = await runOf(adaptersOf, invocation);
|
|
7254
|
+
const { organizationId, account } = await resolveClaudeAccount(run, invocation.positionals[0] ?? "");
|
|
7255
|
+
const renamed = await renamedClaudeAccount(run.api, organizationId, account.id, label);
|
|
7256
|
+
return printed(run, renamed, (shown) => `The Claude account is ${shown.label} now.`);
|
|
7257
|
+
}
|
|
7258
|
+
}),
|
|
7259
|
+
defineCommand({
|
|
7260
|
+
name: "rotate",
|
|
7261
|
+
summary: "Replace the secret of a Claude account.",
|
|
7262
|
+
usage: "claude-accounts rotate <account> [--restart-agents] [--secret-file <path>]",
|
|
7263
|
+
optionsUsage: [
|
|
7264
|
+
...accountUsage$1,
|
|
7265
|
+
["--restart-agents", "restart the agents that work on it once the secret is in"],
|
|
7266
|
+
...secretUsage
|
|
7267
|
+
],
|
|
7268
|
+
options: {
|
|
7269
|
+
...secretOptions,
|
|
7270
|
+
"restart-agents": {
|
|
7271
|
+
type: "boolean",
|
|
7272
|
+
default: false
|
|
7273
|
+
}
|
|
7274
|
+
},
|
|
7275
|
+
positionals: {
|
|
7276
|
+
min: 1,
|
|
7277
|
+
max: 1
|
|
7278
|
+
},
|
|
7279
|
+
run: async (invocation) => {
|
|
7280
|
+
const { values } = invocation;
|
|
7281
|
+
const run = await runOf(adaptersOf, invocation);
|
|
7282
|
+
const { organizationId, account } = await resolveClaudeAccount(run, invocation.positionals[0] ?? "");
|
|
7283
|
+
const secret = await secretOf({
|
|
7284
|
+
run,
|
|
7285
|
+
file: values["secret-file"],
|
|
7286
|
+
fromStdin: values["secret-stdin"],
|
|
7287
|
+
question: `The new secret of ${account.label}`
|
|
7288
|
+
});
|
|
7289
|
+
const rotated = dataOf("rotate the Claude account", await run.api.POST("/organizations/{organizationId}/claude-accounts/{credentialId}/rotate", {
|
|
7290
|
+
params: { path: {
|
|
7291
|
+
organizationId,
|
|
7292
|
+
credentialId: account.id
|
|
7293
|
+
} },
|
|
7294
|
+
body: {
|
|
7295
|
+
secret,
|
|
7296
|
+
restartAgents: values["restart-agents"]
|
|
7297
|
+
}
|
|
7298
|
+
}), claudeAccountHints);
|
|
7299
|
+
return printed(run, rotated, (shown) => `Rotated ${shown.label} to ${shown.fingerprint}${values["restart-agents"] ? "; its agents restart" : ""}.`);
|
|
7300
|
+
}
|
|
7301
|
+
}),
|
|
7302
|
+
defineCommand({
|
|
7303
|
+
name: "verify",
|
|
7304
|
+
summary: "Ask the instance to verify a Claude account against Anthropic.",
|
|
7305
|
+
usage: "claude-accounts verify <account>",
|
|
7306
|
+
optionsUsage: accountUsage$1,
|
|
7307
|
+
options: {},
|
|
7308
|
+
positionals: {
|
|
7309
|
+
min: 1,
|
|
7310
|
+
max: 1
|
|
7311
|
+
},
|
|
7312
|
+
run: async (invocation) => {
|
|
7313
|
+
const run = await runOf(adaptersOf, invocation);
|
|
7314
|
+
const { organizationId, account } = await resolveClaudeAccount(run, invocation.positionals[0] ?? "");
|
|
7315
|
+
await verifyClaudeAccount(run.api, organizationId, account.id);
|
|
7316
|
+
return said(run, `The instance is verifying ${account.label}; kcloud claude-accounts list shows the answer.`);
|
|
7317
|
+
}
|
|
7318
|
+
}),
|
|
7319
|
+
defineCommand({
|
|
7320
|
+
name: "usage",
|
|
7321
|
+
summary: "Show the usage of a Claude subscription.",
|
|
7322
|
+
usage: "claude-accounts usage <account> [--refresh]",
|
|
7323
|
+
optionsUsage: [...accountUsage$1, ["--refresh", "read it from Anthropic now; within 30 seconds the last reading"]],
|
|
7324
|
+
options: { refresh: {
|
|
7325
|
+
type: "boolean",
|
|
7326
|
+
default: false
|
|
7327
|
+
} },
|
|
7328
|
+
positionals: {
|
|
7329
|
+
min: 1,
|
|
7330
|
+
max: 1
|
|
7331
|
+
},
|
|
7332
|
+
run: async (invocation) => {
|
|
7333
|
+
const run = await runOf(adaptersOf, invocation);
|
|
7334
|
+
const { organizationId, account } = await resolveClaudeAccount(run, invocation.positionals[0] ?? "");
|
|
7335
|
+
if (account.kind !== "claude_oauth_token") throw new CliError(`${account.label} is an ${account.kind}, which reports no usage.`, usageUnavailable);
|
|
7336
|
+
const read = invocation.values.refresh ? await refreshedClaudeAccount(run.api, organizationId, account.id) : account;
|
|
7337
|
+
return printed(run, read, usageReportOf);
|
|
7338
|
+
}
|
|
7339
|
+
}),
|
|
7340
|
+
defineCommand({
|
|
7341
|
+
name: "delete",
|
|
7342
|
+
summary: "Delete a Claude account that stands in no queue.",
|
|
7343
|
+
usage: "claude-accounts delete <account>",
|
|
7344
|
+
optionsUsage: accountUsage$1,
|
|
7345
|
+
options: {},
|
|
7346
|
+
positionals: {
|
|
7347
|
+
min: 1,
|
|
7348
|
+
max: 1
|
|
7349
|
+
},
|
|
7350
|
+
run: async (invocation) => {
|
|
7351
|
+
const run = await runOf(adaptersOf, invocation);
|
|
7352
|
+
const reference = invocation.positionals[0] ?? "";
|
|
7353
|
+
const { organizationId, account } = await resolveClaudeAccount(run, reference);
|
|
7354
|
+
await confirmed({
|
|
7355
|
+
prompt: run.prompt,
|
|
7356
|
+
settings: run.settings,
|
|
7357
|
+
question: `Delete the Claude account ${account.label}? The platform erases its secret.`
|
|
7358
|
+
});
|
|
7359
|
+
await deleteClaudeAccount(run.api, organizationId, account.id);
|
|
7360
|
+
return said(run, `Deleted the Claude account ${account.label}.`);
|
|
7361
|
+
}
|
|
7362
|
+
})
|
|
7363
|
+
]
|
|
7364
|
+
});
|
|
7365
|
+
//#endregion
|
|
6733
7366
|
//#region src/mcp/tool.ts
|
|
6734
7367
|
const defineTool = (definition) => ({
|
|
6735
7368
|
...definition,
|
|
@@ -6990,7 +7623,7 @@ const agentTools = [
|
|
|
6990
7623
|
defineTool({
|
|
6991
7624
|
name: "agent_create",
|
|
6992
7625
|
title: "Create an agent",
|
|
6993
|
-
description: "Create an agent in a project. The node it runs on, the credentials, repositories and tools are named by the ids that nodes_list, credentials_list, repositories_list and tools_list return; agent_show with asPlan shows a complete example. requiredCapabilities names what the node has to offer, from the offered list of nodes_list; the instance refuses a node that lacks one. The agent stays stopped until agent_action starts it.",
|
|
7626
|
+
description: "Create an agent in a project. The node it runs on, the git provider credentials, repositories and tools are named by the ids that nodes_list, credentials_list, repositories_list and tools_list return; gitCredentialIds takes one git provider credential for each server of its repositories; agent_show with asPlan shows a complete example. The agent works on the queue of Claude accounts of its project, which project_accounts_list shows. requiredCapabilities names what the node has to offer, from the offered list of nodes_list; the instance refuses a node that lacks one. The agent stays stopped until agent_action starts it.",
|
|
6994
7627
|
operations: ["createAgent"],
|
|
6995
7628
|
safety: "write",
|
|
6996
7629
|
input: z.object({
|
|
@@ -7005,7 +7638,7 @@ const agentTools = [
|
|
|
7005
7638
|
defineTool({
|
|
7006
7639
|
name: "agent_update",
|
|
7007
7640
|
title: "Change an agent",
|
|
7008
|
-
description: "Change sections of an agent. Without version the change applies to the version read just before it. When restartRequired comes back true, agent_action start or update applies the change, whichever availableActions offers.",
|
|
7641
|
+
description: "Change sections of an agent; the credentials section names its git provider credentials as gitCredentialIds, and takes the repositories section in the same change. Without version the change applies to the version read just before it. When restartRequired comes back true, agent_action start or update applies the change, whichever availableActions offers.",
|
|
7009
7642
|
operations: ["updateAgent"],
|
|
7010
7643
|
safety: "write",
|
|
7011
7644
|
input: z.object({
|
|
@@ -7695,13 +8328,311 @@ const agentBackupTools = [
|
|
|
7695
8328
|
})
|
|
7696
8329
|
];
|
|
7697
8330
|
//#endregion
|
|
8331
|
+
//#region src/commands/projects/accounts.ts
|
|
8332
|
+
const projectAccountsHints = {
|
|
8333
|
+
...projectHint,
|
|
8334
|
+
"auth.forbidden": "Changing the queue of a project takes an admin of the project.",
|
|
8335
|
+
"account.not_found": "kcloud claude-accounts list shows the Claude accounts of the organization.",
|
|
8336
|
+
"account.kind_mismatch": "A queue holds Claude subscriptions and Anthropic API keys alone.",
|
|
8337
|
+
"concurrency.conflict": "Someone changed the queue meanwhile; kcloud projects accounts list shows it now."
|
|
8338
|
+
};
|
|
8339
|
+
const projectOption$2 = { project: { type: "string" } };
|
|
8340
|
+
const projectUsage$2 = [["--project <name>", "the project whose queue it is, by slug, name or id"], ["", "an account is named by its label or its id"]];
|
|
8341
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
8342
|
+
const dayOf$1 = (moment) => `${String(moment.getFullYear())}-${pad(moment.getMonth() + 1)}-${pad(moment.getDate())}`;
|
|
8343
|
+
const clockOf = (at, now) => {
|
|
8344
|
+
const moment = new Date(at);
|
|
8345
|
+
const time = `${pad(moment.getHours())}:${pad(moment.getMinutes())}`;
|
|
8346
|
+
return dayOf$1(moment) === dayOf$1(now) ? time : `${dayOf$1(moment)} ${time}`;
|
|
8347
|
+
};
|
|
8348
|
+
const stateOf$1 = ({ state, until }, now) => {
|
|
8349
|
+
switch (state) {
|
|
8350
|
+
case "inUse": return "in use";
|
|
8351
|
+
case "exhausted": return until === null ? "exhausted" : `exhausted until ${clockOf(until, now)}`;
|
|
8352
|
+
case "noCredits": return "no credits";
|
|
8353
|
+
default: return state;
|
|
8354
|
+
}
|
|
8355
|
+
};
|
|
8356
|
+
const waitTextOf = ({ waitReason, nearestResetAt }, now) => {
|
|
8357
|
+
switch (waitReason) {
|
|
8358
|
+
case "exhausted": return nearestResetAt === null ? "Every account of the queue is exhausted; the agents wait for a reset." : `Every account of the queue is exhausted; the agents wait until ${clockOf(nearestResetAt, now)}.`;
|
|
8359
|
+
case "blocked": return "No account of the queue can work: top up the credits of a key, or fix or replace an invalid account.";
|
|
8360
|
+
case "empty": return "The queue is empty, so the agents of the project cannot start; kcloud projects accounts add <account> --project <name> adds one.";
|
|
8361
|
+
default: return null;
|
|
8362
|
+
}
|
|
8363
|
+
};
|
|
8364
|
+
const queueText = (queue, now) => {
|
|
8365
|
+
const wait = waitTextOf(queue, now);
|
|
8366
|
+
if (queue.entries.length === 0) return wait ?? "The queue is empty.";
|
|
8367
|
+
const table = renderTable([
|
|
8368
|
+
{
|
|
8369
|
+
header: "#",
|
|
8370
|
+
align: "right"
|
|
8371
|
+
},
|
|
8372
|
+
{ header: "LABEL" },
|
|
8373
|
+
{ header: "KIND" },
|
|
8374
|
+
{ header: "STATE" },
|
|
8375
|
+
{ header: "USAGE" },
|
|
8376
|
+
{
|
|
8377
|
+
header: "AGENTS",
|
|
8378
|
+
align: "right"
|
|
8379
|
+
},
|
|
8380
|
+
{ header: "ID" }
|
|
8381
|
+
], queue.entries.map((entry) => [
|
|
8382
|
+
String(entry.position),
|
|
8383
|
+
entry.credential.label,
|
|
8384
|
+
entry.credential.kind,
|
|
8385
|
+
stateOf$1(entry, now),
|
|
8386
|
+
usageSummaryOf(entry.credential),
|
|
8387
|
+
String(entry.agents),
|
|
8388
|
+
entry.credential.id
|
|
8389
|
+
]));
|
|
8390
|
+
return wait === null ? table : `${table}\n\n${wait}`;
|
|
8391
|
+
};
|
|
8392
|
+
const projectClaudeAccountsOf = async (api, projectId) => dataOf("list the queue of Claude accounts of the project", await api.GET("/projects/{id}/claude-accounts", { params: { path: { id: projectId } } }), projectAccountsHints);
|
|
8393
|
+
const queueSet = async (api, projectId, credentialIds, version) => dataOf("set the queue of Claude accounts of the project", await api.PUT("/projects/{id}/claude-accounts", {
|
|
8394
|
+
params: { path: { id: projectId } },
|
|
8395
|
+
body: {
|
|
8396
|
+
credentialIds: [...credentialIds],
|
|
8397
|
+
version
|
|
8398
|
+
}
|
|
8399
|
+
}), projectAccountsHints);
|
|
8400
|
+
const organizationAccountIds = async (api, projectId, references) => {
|
|
8401
|
+
if (references.length === 0) return [];
|
|
8402
|
+
const { organizationId } = await projectOf(api, projectId);
|
|
8403
|
+
const accounts = await claudeAccountsOf(api, organizationId);
|
|
8404
|
+
const ids = references.map((reference) => claudeAccountIn(accounts, reference).id);
|
|
8405
|
+
const twice = references.filter((_, index) => ids.indexOf(ids[index] ?? "") !== index);
|
|
8406
|
+
if (twice.length > 0) throw new UsageError(`${twice.join(", ")} ${twice.length === 1 ? "names an account" : "name accounts"} named before.`, "A queue holds each account once.");
|
|
8407
|
+
return ids;
|
|
8408
|
+
};
|
|
8409
|
+
const queuedIndexOf = (queue, reference) => {
|
|
8410
|
+
const found = queue.entries.findIndex(({ credential }) => credential.id === reference.toLowerCase() || credential.label === reference);
|
|
8411
|
+
if (found === -1) throw new CliError(`${reference} stands in no place of the queue.`, queue.entries.length === 0 ? "The queue is empty." : `The queue holds ${queue.entries.map(({ credential }) => credential.label).join(", ")}.`);
|
|
8412
|
+
return found;
|
|
8413
|
+
};
|
|
8414
|
+
const idsOf = (queue) => queue.entries.map(({ credential }) => credential.id);
|
|
8415
|
+
const queueWith = async (api, projectId, reference) => {
|
|
8416
|
+
const queue = await projectClaudeAccountsOf(api, projectId);
|
|
8417
|
+
const [id = ""] = await organizationAccountIds(api, projectId, [reference]);
|
|
8418
|
+
const place = queue.entries.find(({ credential }) => credential.id === id);
|
|
8419
|
+
if (place !== void 0) throw new CliError(`${place.credential.label} already stands at place ${String(place.position)} of the queue.`, "kcloud projects accounts move <account> --to <n> --project <name> moves it.");
|
|
8420
|
+
return queueSet(api, projectId, [...idsOf(queue), id], queue.version);
|
|
8421
|
+
};
|
|
8422
|
+
const queueWithout = async (api, projectId, reference) => {
|
|
8423
|
+
const queue = await projectClaudeAccountsOf(api, projectId);
|
|
8424
|
+
const index = queuedIndexOf(queue, reference);
|
|
8425
|
+
const ids = idsOf(queue);
|
|
8426
|
+
ids.splice(index, 1);
|
|
8427
|
+
return queueSet(api, projectId, ids, queue.version);
|
|
8428
|
+
};
|
|
8429
|
+
const queueMoved = async (api, projectId, reference, place) => {
|
|
8430
|
+
const queue = await projectClaudeAccountsOf(api, projectId);
|
|
8431
|
+
if (place > queue.entries.length) throw new UsageError(`--to ${String(place)} lies beyond the queue, which holds ${String(queue.entries.length)} account${queue.entries.length === 1 ? "" : "s"}.`);
|
|
8432
|
+
const ids = idsOf(queue);
|
|
8433
|
+
const [moved = ""] = ids.splice(queuedIndexOf(queue, reference), 1);
|
|
8434
|
+
ids.splice(place - 1, 0, moved);
|
|
8435
|
+
return queueSet(api, projectId, ids, queue.version);
|
|
8436
|
+
};
|
|
8437
|
+
const placeOf = (given) => {
|
|
8438
|
+
if (given === void 0) throw new UsageError("projects accounts move needs --to <n>.");
|
|
8439
|
+
if (!/^[0-9]+$/u.test(given) || Number(given) < 1) throw new UsageError(`--to ${given} is not a place in the queue, counted from 1.`);
|
|
8440
|
+
return Number(given);
|
|
8441
|
+
};
|
|
8442
|
+
const projectFrom$2 = async (scope, given) => {
|
|
8443
|
+
if (given === void 0) throw new UsageError("This command needs a project.", "Pass --project <name>; kcloud projects list shows them.");
|
|
8444
|
+
return resolveProjectId(scope, given);
|
|
8445
|
+
};
|
|
8446
|
+
const printedQueue = (run, queue) => printed(run, queue, (shown) => queueText(shown, run.adapters.now()));
|
|
8447
|
+
const projectAccountsCommand = (adaptersOf) => defineGroup({
|
|
8448
|
+
name: "accounts",
|
|
8449
|
+
summary: "The queue of Claude accounts the agents of a project work on.",
|
|
8450
|
+
usage: "projects accounts <command>",
|
|
8451
|
+
subcommands: [
|
|
8452
|
+
defineCommand({
|
|
8453
|
+
name: "list",
|
|
8454
|
+
summary: "List the queue of Claude accounts of a project with their state.",
|
|
8455
|
+
usage: "projects accounts list --project <name>",
|
|
8456
|
+
optionsUsage: projectUsage$2,
|
|
8457
|
+
options: projectOption$2,
|
|
8458
|
+
positionals: {
|
|
8459
|
+
min: 0,
|
|
8460
|
+
max: 0
|
|
8461
|
+
},
|
|
8462
|
+
run: async (invocation) => {
|
|
8463
|
+
const run = await runOf(adaptersOf, invocation);
|
|
8464
|
+
const projectId = await projectFrom$2(run, invocation.values.project);
|
|
8465
|
+
return printedQueue(run, await projectClaudeAccountsOf(run.api, projectId));
|
|
8466
|
+
}
|
|
8467
|
+
}),
|
|
8468
|
+
defineCommand({
|
|
8469
|
+
name: "set",
|
|
8470
|
+
summary: "Set the whole queue of Claude accounts of a project, in order.",
|
|
8471
|
+
usage: "projects accounts set [<account>...] --project <name>",
|
|
8472
|
+
optionsUsage: [...projectUsage$2, ["", "the first account named comes first; none empties the queue"]],
|
|
8473
|
+
options: projectOption$2,
|
|
8474
|
+
positionals: {
|
|
8475
|
+
min: 0,
|
|
8476
|
+
max: 50
|
|
8477
|
+
},
|
|
8478
|
+
run: async (invocation) => {
|
|
8479
|
+
const run = await runOf(adaptersOf, invocation);
|
|
8480
|
+
const projectId = await projectFrom$2(run, invocation.values.project);
|
|
8481
|
+
const ids = await organizationAccountIds(run.api, projectId, invocation.positionals);
|
|
8482
|
+
const { version } = await projectClaudeAccountsOf(run.api, projectId);
|
|
8483
|
+
return printedQueue(run, await queueSet(run.api, projectId, ids, version));
|
|
8484
|
+
}
|
|
8485
|
+
}),
|
|
8486
|
+
defineCommand({
|
|
8487
|
+
name: "add",
|
|
8488
|
+
summary: "Put a Claude account of the organization at the end of the queue.",
|
|
8489
|
+
usage: "projects accounts add <account> --project <name>",
|
|
8490
|
+
optionsUsage: projectUsage$2,
|
|
8491
|
+
options: projectOption$2,
|
|
8492
|
+
positionals: {
|
|
8493
|
+
min: 1,
|
|
8494
|
+
max: 1
|
|
8495
|
+
},
|
|
8496
|
+
run: async (invocation) => {
|
|
8497
|
+
const run = await runOf(adaptersOf, invocation);
|
|
8498
|
+
const projectId = await projectFrom$2(run, invocation.values.project);
|
|
8499
|
+
return printedQueue(run, await queueWith(run.api, projectId, invocation.positionals[0] ?? ""));
|
|
8500
|
+
}
|
|
8501
|
+
}),
|
|
8502
|
+
defineCommand({
|
|
8503
|
+
name: "remove",
|
|
8504
|
+
summary: "Take a Claude account out of the queue; it stays in the organization.",
|
|
8505
|
+
usage: "projects accounts remove <account> --project <name>",
|
|
8506
|
+
optionsUsage: projectUsage$2,
|
|
8507
|
+
options: projectOption$2,
|
|
8508
|
+
positionals: {
|
|
8509
|
+
min: 1,
|
|
8510
|
+
max: 1
|
|
8511
|
+
},
|
|
8512
|
+
run: async (invocation) => {
|
|
8513
|
+
const run = await runOf(adaptersOf, invocation);
|
|
8514
|
+
const projectId = await projectFrom$2(run, invocation.values.project);
|
|
8515
|
+
return printedQueue(run, await queueWithout(run.api, projectId, invocation.positionals[0] ?? ""));
|
|
8516
|
+
}
|
|
8517
|
+
}),
|
|
8518
|
+
defineCommand({
|
|
8519
|
+
name: "move",
|
|
8520
|
+
summary: "Move a Claude account to another place of the queue.",
|
|
8521
|
+
usage: "projects accounts move <account> --to <n> --project <name>",
|
|
8522
|
+
optionsUsage: [...projectUsage$2, ["--to <n>", "the new place, counted from 1"]],
|
|
8523
|
+
options: {
|
|
8524
|
+
...projectOption$2,
|
|
8525
|
+
to: { type: "string" }
|
|
8526
|
+
},
|
|
8527
|
+
positionals: {
|
|
8528
|
+
min: 1,
|
|
8529
|
+
max: 1
|
|
8530
|
+
},
|
|
8531
|
+
run: async (invocation) => {
|
|
8532
|
+
const place = placeOf(invocation.values.to);
|
|
8533
|
+
const run = await runOf(adaptersOf, invocation);
|
|
8534
|
+
const projectId = await projectFrom$2(run, invocation.values.project);
|
|
8535
|
+
return printedQueue(run, await queueMoved(run.api, projectId, invocation.positionals[0] ?? "", place));
|
|
8536
|
+
}
|
|
8537
|
+
})
|
|
8538
|
+
]
|
|
8539
|
+
});
|
|
8540
|
+
//#endregion
|
|
8541
|
+
//#region src/mcp/tools/claude-accounts.ts
|
|
8542
|
+
const accountField$1 = z.string().trim().min(1).describe("The Claude account, by its label or its id, from claude_accounts_list.");
|
|
8543
|
+
const claudeAccountTools = [
|
|
8544
|
+
defineTool({
|
|
8545
|
+
name: "claude_accounts_list",
|
|
8546
|
+
title: "List the Claude accounts of the organization",
|
|
8547
|
+
description: "List the Claude subscriptions and Anthropic API keys of the organization this server acts in, with their fingerprint, verification, usage and the queues of the projects they stand in, never their secrets.",
|
|
8548
|
+
operations: ["listClaudeAccounts"],
|
|
8549
|
+
safety: "read",
|
|
8550
|
+
input: z.object({}),
|
|
8551
|
+
run: async (_, run) => claudeAccountsOf(run.api, (await run.organization()).id)
|
|
8552
|
+
}),
|
|
8553
|
+
defineTool({
|
|
8554
|
+
name: "claude_account_update",
|
|
8555
|
+
title: "Rename, verify or read the usage of a Claude account",
|
|
8556
|
+
description: "rename gives a Claude account a new label; verify asks the instance to check it against Anthropic, and claude_accounts_list shows the answer; refresh_usage reads the usage of a Claude subscription now, or answers the last reading within 30 seconds of the previous one. Each action takes an admin of the organization.",
|
|
8557
|
+
operations: [
|
|
8558
|
+
"renameClaudeAccount",
|
|
8559
|
+
"verifyClaudeAccount",
|
|
8560
|
+
"refreshClaudeAccountUsage"
|
|
8561
|
+
],
|
|
8562
|
+
safety: "write",
|
|
8563
|
+
input: z.object({
|
|
8564
|
+
account: accountField$1,
|
|
8565
|
+
action: z.enum([
|
|
8566
|
+
"rename",
|
|
8567
|
+
"verify",
|
|
8568
|
+
"refresh_usage"
|
|
8569
|
+
]),
|
|
8570
|
+
label: z.string().trim().min(1).optional().describe("The new label, for rename.")
|
|
8571
|
+
}),
|
|
8572
|
+
run: async ({ account, action, label }, run) => {
|
|
8573
|
+
if (action === "rename" && label === void 0) throw new UsageError("rename needs a label.");
|
|
8574
|
+
const { organizationId, account: found } = await resolveClaudeAccount(run, account);
|
|
8575
|
+
if (action === "refresh_usage") return refreshedClaudeAccount(run.api, organizationId, found.id);
|
|
8576
|
+
if (action === "verify") {
|
|
8577
|
+
await verifyClaudeAccount(run.api, organizationId, found.id);
|
|
8578
|
+
return {
|
|
8579
|
+
id: found.id,
|
|
8580
|
+
verifying: true
|
|
8581
|
+
};
|
|
8582
|
+
}
|
|
8583
|
+
return renamedClaudeAccount(run.api, organizationId, found.id, label ?? "");
|
|
8584
|
+
}
|
|
8585
|
+
}),
|
|
8586
|
+
defineTool({
|
|
8587
|
+
name: "claude_account_delete",
|
|
8588
|
+
title: "Delete a Claude account",
|
|
8589
|
+
description: "Delete a Claude account of the organization and erase its secret. The instance refuses while it stands in the queue of a project, and names those projects. Takes an admin of the organization.",
|
|
8590
|
+
operations: ["deleteClaudeAccount"],
|
|
8591
|
+
safety: "destructive",
|
|
8592
|
+
input: z.object({ account: accountField$1 }),
|
|
8593
|
+
run: async ({ account }, run) => {
|
|
8594
|
+
const { organizationId, account: found } = await resolveClaudeAccount(run, account);
|
|
8595
|
+
await deleteClaudeAccount(run.api, organizationId, found.id);
|
|
8596
|
+
return { deleted: found.id };
|
|
8597
|
+
}
|
|
8598
|
+
}),
|
|
8599
|
+
defineTool({
|
|
8600
|
+
name: "project_accounts_list",
|
|
8601
|
+
title: "List the queue of Claude accounts of a project",
|
|
8602
|
+
description: "List the queue of Claude accounts the agents of a project work on, in order, each with its state: inUse, available, exhausted until a moment, noCredits or invalid. activeCredentialId names the account in use, and waitReason says why the agents wait: exhausted, blocked or empty.",
|
|
8603
|
+
operations: ["listProjectClaudeAccounts"],
|
|
8604
|
+
safety: "read",
|
|
8605
|
+
input: z.object({ project: projectField }),
|
|
8606
|
+
run: async ({ project }, run) => projectClaudeAccountsOf(run.api, await projectIdOf(run, project))
|
|
8607
|
+
}),
|
|
8608
|
+
defineTool({
|
|
8609
|
+
name: "project_accounts_set",
|
|
8610
|
+
title: "Set the queue of Claude accounts of a project",
|
|
8611
|
+
description: "Set the whole queue of Claude accounts of a project, in order: the first account comes first, an account left out leaves the queue and stays in the organization, and an empty list empties the queue. Without version the change applies to the queue read just before it. Takes an admin of the project.",
|
|
8612
|
+
operations: ["listProjectClaudeAccounts", "setProjectClaudeAccounts"],
|
|
8613
|
+
safety: "write",
|
|
8614
|
+
idempotent: true,
|
|
8615
|
+
input: z.object({
|
|
8616
|
+
project: projectField,
|
|
8617
|
+
accounts: z.array(accountField$1).max(50).describe("The Claude accounts of the queue, by label or id, first one first."),
|
|
8618
|
+
version: z.int().nonnegative().optional().describe("The version of the queue the change is based on, to refuse a change made meanwhile.")
|
|
8619
|
+
}),
|
|
8620
|
+
run: async ({ project, accounts, version }, run) => {
|
|
8621
|
+
const projectId = await projectIdOf(run, project);
|
|
8622
|
+
const ids = await organizationAccountIds(run.api, projectId, accounts);
|
|
8623
|
+
const based = version ?? (await projectClaudeAccountsOf(run.api, projectId)).version;
|
|
8624
|
+
return queueSet(run.api, projectId, ids, based);
|
|
8625
|
+
}
|
|
8626
|
+
})
|
|
8627
|
+
];
|
|
8628
|
+
//#endregion
|
|
7698
8629
|
//#region src/mcp/tools/credentials.ts
|
|
7699
8630
|
const credentialField$1 = z.string().trim().min(1).describe("The credential, by its label or its id.");
|
|
7700
8631
|
const credentialTools = [
|
|
7701
8632
|
defineTool({
|
|
7702
8633
|
name: "credentials_list",
|
|
7703
8634
|
title: "List credentials",
|
|
7704
|
-
description: "List the
|
|
8635
|
+
description: "List the git provider credentials of a project (GitHub, GitLab and Bitbucket), with their server, account, commit identity, fingerprints and verification, never their secrets. The Claude accounts belong to the organization: claude_accounts_list.",
|
|
7705
8636
|
operations: ["listCredentials"],
|
|
7706
8637
|
safety: "read",
|
|
7707
8638
|
input: z.object({ project: projectField }),
|
|
@@ -7709,11 +8640,12 @@ const credentialTools = [
|
|
|
7709
8640
|
}),
|
|
7710
8641
|
defineTool({
|
|
7711
8642
|
name: "credential_update",
|
|
7712
|
-
title: "Rename, verify or read the usage of a credential",
|
|
7713
|
-
description: "rename gives a credential a new label; verify asks the instance to check it against its service, and credentials_list shows the answer; refresh_usage reads the usage of the Claude subscription behind a Claude Code OAuth token now, or answers the last reading within 30 seconds of the previous one.",
|
|
8643
|
+
title: "Rename, verify, sign commits as or read the usage of a credential",
|
|
8644
|
+
description: "rename gives a credential a new label; verify asks the instance to check it against its service, and credentials_list shows the answer; set_identity sets the commitName and commitEmail the commits of a git provider token carry, which the agents that use it pick up after a restart; refresh_usage reads the usage of the Claude subscription behind a Claude Code OAuth token now, or answers the last reading within 30 seconds of the previous one.",
|
|
7714
8645
|
operations: [
|
|
7715
8646
|
"renameCredential",
|
|
7716
8647
|
"verifyCredential",
|
|
8648
|
+
"setCredentialCommitIdentity",
|
|
7717
8649
|
"refreshCredentialUsage"
|
|
7718
8650
|
],
|
|
7719
8651
|
safety: "write",
|
|
@@ -7723,11 +8655,14 @@ const credentialTools = [
|
|
|
7723
8655
|
action: z.enum([
|
|
7724
8656
|
"rename",
|
|
7725
8657
|
"verify",
|
|
8658
|
+
"set_identity",
|
|
7726
8659
|
"refresh_usage"
|
|
7727
8660
|
]),
|
|
7728
|
-
label: z.string().trim().min(1).optional().describe("The new label, for rename.")
|
|
8661
|
+
label: z.string().trim().min(1).optional().describe("The new label, for rename."),
|
|
8662
|
+
commitName: commitNameSchema.optional().describe("The name its commits carry, for set_identity."),
|
|
8663
|
+
commitEmail: commitEmailSchema.optional().describe("The e-mail its commits carry, for set_identity.")
|
|
7729
8664
|
}),
|
|
7730
|
-
run: async ({ project, credential, action, label }, run) => {
|
|
8665
|
+
run: async ({ project, credential, action, label, commitName, commitEmail }, run) => {
|
|
7731
8666
|
const projectId = await projectIdOf(run, project);
|
|
7732
8667
|
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
7733
8668
|
if (action === "refresh_usage") return dataOf("read the usage of the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/usage", { params: { path: {
|
|
@@ -7744,6 +8679,19 @@ const credentialTools = [
|
|
|
7744
8679
|
verifying: true
|
|
7745
8680
|
};
|
|
7746
8681
|
}
|
|
8682
|
+
if (action === "set_identity") {
|
|
8683
|
+
if (commitName === void 0 || commitEmail === void 0) throw new UsageError("set_identity needs a commitName and a commitEmail.");
|
|
8684
|
+
return dataOf("set the commit identity of the credential", await run.api.PUT("/projects/{id}/credentials/{credentialId}/commit-identity", {
|
|
8685
|
+
params: { path: {
|
|
8686
|
+
id: projectId,
|
|
8687
|
+
credentialId
|
|
8688
|
+
} },
|
|
8689
|
+
body: {
|
|
8690
|
+
commitName,
|
|
8691
|
+
commitEmail
|
|
8692
|
+
}
|
|
8693
|
+
}), credentialHints);
|
|
8694
|
+
}
|
|
7747
8695
|
if (label === void 0) throw new UsageError("rename needs a label.");
|
|
7748
8696
|
return dataOf("rename the credential", await run.api.PATCH("/projects/{id}/credentials/{credentialId}", {
|
|
7749
8697
|
params: { path: {
|
|
@@ -8528,6 +9476,12 @@ const costsText = (costs, { from, to }, slugs) => [
|
|
|
8528
9476
|
"",
|
|
8529
9477
|
`${costs.totalCostUsd.toFixed(2)} over ${costs.totalRuns} runs and ${costs.sessions} sessions.`
|
|
8530
9478
|
].join("\n");
|
|
9479
|
+
const switchThresholdOf = (given) => {
|
|
9480
|
+
if (given === void 0) return void 0;
|
|
9481
|
+
const threshold = Number(given);
|
|
9482
|
+
if (!/^[0-9]+$/u.test(given) || threshold < 1 || threshold > 100) throw new UsageError(`--switch-threshold ${given} is not a whole percent from 1 to 100.`);
|
|
9483
|
+
return threshold;
|
|
9484
|
+
};
|
|
8531
9485
|
const organizationChangeHints = {
|
|
8532
9486
|
...organizationHints,
|
|
8533
9487
|
"organization.slug_taken": "Another organization already uses that slug.",
|
|
@@ -8548,12 +9502,14 @@ const updatedOrganization = async (api, current, fields) => {
|
|
|
8548
9502
|
if ([
|
|
8549
9503
|
fields.name,
|
|
8550
9504
|
fields.slug,
|
|
8551
|
-
fields.color
|
|
9505
|
+
fields.color,
|
|
9506
|
+
fields.switchThreshold
|
|
8552
9507
|
].every((field) => field === void 0)) return current;
|
|
8553
9508
|
const body = {
|
|
8554
9509
|
name: fields.name ?? current.name,
|
|
8555
9510
|
slug: fields.slug ?? current.slug,
|
|
8556
|
-
color: fields.color ?? current.color
|
|
9511
|
+
color: fields.color ?? current.color,
|
|
9512
|
+
...fields.switchThreshold === void 0 ? {} : { claudeSwitchThreshold: fields.switchThreshold }
|
|
8557
9513
|
};
|
|
8558
9514
|
validated(updateOrganizationBodySchema, body, "a change to an organization");
|
|
8559
9515
|
return dataOf("change the organization", await api.PATCH("/organizations/{organizationId}", {
|
|
@@ -8656,6 +9612,7 @@ const createOrgsCommand = (adaptersOf) => defineGroup({
|
|
|
8656
9612
|
["slug", shown.slug],
|
|
8657
9613
|
["color", shown.color],
|
|
8658
9614
|
["logo", shown.logoHash ?? "-"],
|
|
9615
|
+
["switch threshold", `${String(shown.claudeSwitchThreshold)}%`],
|
|
8659
9616
|
["created", shown.createdAt],
|
|
8660
9617
|
["id", shown.id]
|
|
8661
9618
|
]));
|
|
@@ -8663,18 +9620,20 @@ const createOrgsCommand = (adaptersOf) => defineGroup({
|
|
|
8663
9620
|
}),
|
|
8664
9621
|
defineCommand({
|
|
8665
9622
|
name: "set",
|
|
8666
|
-
summary: "Change the name, slug, color or
|
|
8667
|
-
usage: "orgs set [<organization>] [--name <name>] [--slug <slug>] [--color <#rrggbb>] [--logo <path> | --no-logo]",
|
|
9623
|
+
summary: "Change the name, slug, color, logo or Claude switch threshold of an organization.",
|
|
9624
|
+
usage: "orgs set [<organization>] [--name <name>] [--slug <slug>] [--color <#rrggbb>] [--logo <path> | --no-logo] [--switch-threshold <percent>]",
|
|
8668
9625
|
optionsUsage: [
|
|
8669
9626
|
["--name <name>", "the new name"],
|
|
8670
9627
|
["--slug <slug>", "the new slug; scripts and --org that name the old one stop finding it"],
|
|
8671
9628
|
["--color <#rrggbb>", "the color of the organization in the panel"],
|
|
8672
|
-
...logoUsage
|
|
9629
|
+
...logoUsage,
|
|
9630
|
+
["--switch-threshold <percent>", `the usage at which a project moves to its next Claude account, 1 to 100`]
|
|
8673
9631
|
],
|
|
8674
9632
|
options: {
|
|
8675
9633
|
name: { type: "string" },
|
|
8676
9634
|
slug: { type: "string" },
|
|
8677
9635
|
color: { type: "string" },
|
|
9636
|
+
"switch-threshold": { type: "string" },
|
|
8678
9637
|
...logoOptions
|
|
8679
9638
|
},
|
|
8680
9639
|
positionals: {
|
|
@@ -8683,15 +9642,20 @@ const createOrgsCommand = (adaptersOf) => defineGroup({
|
|
|
8683
9642
|
},
|
|
8684
9643
|
run: async (invocation) => {
|
|
8685
9644
|
const { values } = invocation;
|
|
9645
|
+
const switchThreshold = switchThresholdOf(values["switch-threshold"]);
|
|
8686
9646
|
if ([
|
|
8687
9647
|
values.name,
|
|
8688
9648
|
values.slug,
|
|
8689
|
-
values.color
|
|
8690
|
-
|
|
9649
|
+
values.color,
|
|
9650
|
+
switchThreshold
|
|
9651
|
+
].every((value) => value === void 0) && !isLogoChange(values)) throw new UsageError("orgs set needs --name, --slug, --color, --logo, --no-logo or --switch-threshold.");
|
|
8691
9652
|
const logo = await logoActionOf(invocation.context.workingDirectory, values);
|
|
8692
9653
|
const run = await runOf(adaptersOf, invocation);
|
|
8693
9654
|
const current = await organizationOrInUse(run, invocation.positionals[0]);
|
|
8694
|
-
const updated = await updatedOrganization(run.api, current,
|
|
9655
|
+
const updated = await updatedOrganization(run.api, current, {
|
|
9656
|
+
...values,
|
|
9657
|
+
switchThreshold
|
|
9658
|
+
});
|
|
8695
9659
|
if (updated.slug !== current.slug) {
|
|
8696
9660
|
await followSlug(run.context.homeDirectory, run.origin, current, updated);
|
|
8697
9661
|
run.context.terminal.error(run.context.terminal.errorStyles.warning(`warning Scripts that pass --org ${current.slug} or set ${organizationVariable}=${current.slug} need ${updated.slug} now.`));
|
|
@@ -8767,14 +9731,15 @@ const organizationTools = [
|
|
|
8767
9731
|
defineTool({
|
|
8768
9732
|
name: "organization_update",
|
|
8769
9733
|
title: "Change an organization",
|
|
8770
|
-
description: "Change the name
|
|
9734
|
+
description: "Change the name, the color or the Claude switch threshold of an organization, by default the one this server acts in. A field left out keeps its value. A new slug is set with kcloud orgs set or in the panel.",
|
|
8771
9735
|
operations: ["listOrganizations", "updateOrganization"],
|
|
8772
9736
|
safety: "write",
|
|
8773
9737
|
idempotent: true,
|
|
8774
9738
|
input: z.object({
|
|
8775
9739
|
organization: z.string().trim().min(1).optional().describe("The organization, by its slug, its name or its id."),
|
|
8776
9740
|
name: organizationNameSchema.optional(),
|
|
8777
|
-
color: colorSchema.optional().describe("The color in the panel, as #rrggbb.")
|
|
9741
|
+
color: colorSchema.optional().describe("The color in the panel, as #rrggbb."),
|
|
9742
|
+
switchThreshold: switchThresholdSchema.optional().describe("The used share of a subscription, in percent, at which a project moves to its next Claude account; 100 moves only at the limit.")
|
|
8778
9743
|
}),
|
|
8779
9744
|
run: async ({ organization, ...change }, run) => updatedOrganization(run.api, await organizationOrInUse(run, organization), change)
|
|
8780
9745
|
}),
|
|
@@ -9484,7 +10449,8 @@ const createProjectsCommand = (adaptersOf) => defineGroup({
|
|
|
9484
10449
|
return said(run, `Deleted the project ${project.name}.`);
|
|
9485
10450
|
}
|
|
9486
10451
|
}),
|
|
9487
|
-
projectMembersCommand(adaptersOf)
|
|
10452
|
+
projectMembersCommand(adaptersOf),
|
|
10453
|
+
projectAccountsCommand(adaptersOf)
|
|
9488
10454
|
]
|
|
9489
10455
|
});
|
|
9490
10456
|
//#endregion
|
|
@@ -9683,14 +10649,17 @@ const releaseTools = [defineTool({
|
|
|
9683
10649
|
//#region src/commands/repositories.ts
|
|
9684
10650
|
const projectOption = { project: { type: "string" } };
|
|
9685
10651
|
const projectUsage = [["--project <name>", "the project the repository belongs to, by slug, name or id"]];
|
|
10652
|
+
const providerOf = (details) => providerNameOf(details.provider);
|
|
9686
10653
|
const repositoryHints = {
|
|
9687
10654
|
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
9688
10655
|
"repository.not_found": "kcloud repositories list --project <name> shows them.",
|
|
9689
10656
|
"repository.already_registered": "The project already has that repository.",
|
|
9690
10657
|
"repository.in_use": "Take it off its agents first.",
|
|
9691
|
-
"repository.unreachable":
|
|
9692
|
-
"
|
|
9693
|
-
"credential.
|
|
10658
|
+
"repository.unreachable": (details) => `The credential cannot see it on ${providerOf(details)}; kcloud repositories available lists what it sees.`,
|
|
10659
|
+
"repository.catalog_unavailable": ({ provider, serverUrl }) => `${providerNameOf(provider)} at ${String(serverUrl)} did not return the repositories of the credential. kcloud repositories add <owner>/<name> --credential <credential> registers one by its path.`,
|
|
10660
|
+
"credential.kind_mismatch": "A repository needs the credential of a git provider: a github_pat, gitlab_token or bitbucket_token.",
|
|
10661
|
+
"credential.rejected": (details) => `${providerOf(details)} refused that credential; kcloud credentials rotate <credential> --project <name> replaces it.`,
|
|
10662
|
+
"credential.server_mismatch": ({ provider, serverUrl }) => `The repository is on ${providerNameOf(provider)} at ${String(serverUrl)}; the credential belongs to another provider or server. The SERVER column of kcloud credentials list shows where each one acts.`,
|
|
9694
10663
|
"credential.not_found": "kcloud credentials list --project <name> shows them."
|
|
9695
10664
|
};
|
|
9696
10665
|
const projectFrom = async (run, given) => {
|
|
@@ -9707,13 +10676,15 @@ const resolveRepositoryId = async (api, projectId, reference) => {
|
|
|
9707
10676
|
return first.id;
|
|
9708
10677
|
};
|
|
9709
10678
|
const ownerAndName = (reference) => {
|
|
9710
|
-
const
|
|
9711
|
-
|
|
10679
|
+
const separator = reference.lastIndexOf("/");
|
|
10680
|
+
const owner = reference.slice(0, separator);
|
|
10681
|
+
const name = reference.slice(separator + 1);
|
|
10682
|
+
if (separator < 1 || !isRepositoryOwner(owner) || !repositoryNamePattern.test(name)) throw new UsageError(`${reference} is not a repository path.`, "Name it as owner/name, or group/subgroup/name on GitLab, with up to 20 names before the last.");
|
|
9712
10683
|
return [owner, name];
|
|
9713
10684
|
};
|
|
9714
10685
|
const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
9715
10686
|
name: "repositories",
|
|
9716
|
-
summary: "The GitHub repositories of a project.",
|
|
10687
|
+
summary: "The GitHub, GitLab and Bitbucket repositories of a project.",
|
|
9717
10688
|
usage: "repositories <command>",
|
|
9718
10689
|
subcommands: [
|
|
9719
10690
|
defineCommand({
|
|
@@ -9732,6 +10703,8 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9732
10703
|
const items = await repositoriesOf(run.api, projectId);
|
|
9733
10704
|
return printed(run, items, (repositories) => repositories.length === 0 ? "The project has no repository yet." : renderTable([
|
|
9734
10705
|
{ header: "REPOSITORY" },
|
|
10706
|
+
{ header: "PROVIDER" },
|
|
10707
|
+
{ header: "SERVER" },
|
|
9735
10708
|
{ header: "BRANCH" },
|
|
9736
10709
|
{ header: "VISIBILITY" },
|
|
9737
10710
|
{ header: "STATUS" },
|
|
@@ -9739,6 +10712,8 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9739
10712
|
{ header: "ID" }
|
|
9740
10713
|
], repositories.map((repository) => [
|
|
9741
10714
|
`${repository.owner}/${repository.name}`,
|
|
10715
|
+
repository.provider,
|
|
10716
|
+
repository.serverUrl,
|
|
9742
10717
|
repository.defaultBranch,
|
|
9743
10718
|
repository.isPrivate ? "private" : "public",
|
|
9744
10719
|
repository.status,
|
|
@@ -9749,9 +10724,9 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9749
10724
|
}),
|
|
9750
10725
|
defineCommand({
|
|
9751
10726
|
name: "available",
|
|
9752
|
-
summary: "List the
|
|
10727
|
+
summary: "List the repositories one git provider credential can see.",
|
|
9753
10728
|
usage: "repositories available --project <name> --credential <label>",
|
|
9754
|
-
optionsUsage: [...projectUsage, ["--credential <label>", "the
|
|
10729
|
+
optionsUsage: [...projectUsage, ["--credential <label>", "the git provider credential to look through"]],
|
|
9755
10730
|
options: {
|
|
9756
10731
|
...projectOption,
|
|
9757
10732
|
credential: { type: "string" }
|
|
@@ -9766,7 +10741,7 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9766
10741
|
const credential = invocation.values.credential;
|
|
9767
10742
|
if (credential === void 0) throw new UsageError("repositories available needs --credential <label>.", "kcloud credentials list --project <name> shows them.");
|
|
9768
10743
|
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
9769
|
-
const listed = dataOf("list the repositories of the credential", await run.api.GET("/projects/{id}/repositories/
|
|
10744
|
+
const listed = dataOf("list the repositories of the credential", await run.api.GET("/projects/{id}/repositories/available", { params: {
|
|
9770
10745
|
path: { id: projectId },
|
|
9771
10746
|
query: { credentialId }
|
|
9772
10747
|
} }), repositoryHints);
|
|
@@ -9776,16 +10751,20 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9776
10751
|
{ header: "VISIBILITY" }
|
|
9777
10752
|
], page.items.map((repository) => [
|
|
9778
10753
|
`${repository.owner}/${repository.name}`,
|
|
9779
|
-
repository.defaultBranch,
|
|
10754
|
+
repository.defaultBranch ?? "-",
|
|
9780
10755
|
repository.isPrivate ? "private" : "public"
|
|
9781
|
-
])), ...page.truncated ? ["",
|
|
10756
|
+
])), ...page.truncated ? ["", `${providerNameOf(page.provider)} returned only the first pages.`] : []].join("\n"));
|
|
9782
10757
|
}
|
|
9783
10758
|
}),
|
|
9784
10759
|
defineCommand({
|
|
9785
10760
|
name: "add",
|
|
9786
|
-
summary: "Register a
|
|
10761
|
+
summary: "Register a repository of a git provider in a project.",
|
|
9787
10762
|
usage: "repositories add <owner/name> --project <name> --credential <label>",
|
|
9788
|
-
optionsUsage: [
|
|
10763
|
+
optionsUsage: [
|
|
10764
|
+
...projectUsage,
|
|
10765
|
+
["--credential <label>", "the git provider credential that can see it; it sets the provider and the server"],
|
|
10766
|
+
["", "a GitLab subgroup belongs to the owner: group/subgroup/name; Bitbucket Data Center takes KEY/name"]
|
|
10767
|
+
],
|
|
9789
10768
|
options: {
|
|
9790
10769
|
...projectOption,
|
|
9791
10770
|
credential: { type: "string" }
|
|
@@ -9816,7 +10795,7 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9816
10795
|
name: "set-credential",
|
|
9817
10796
|
summary: "Point a repository at another credential.",
|
|
9818
10797
|
usage: "repositories set-credential <owner/name> --project <name> --credential <label>",
|
|
9819
|
-
optionsUsage: [...projectUsage, ["--credential <label>", "the
|
|
10798
|
+
optionsUsage: [...projectUsage, ["--credential <label>", "the credential to use from now on, of the provider and server of the repository"]],
|
|
9820
10799
|
options: {
|
|
9821
10800
|
...projectOption,
|
|
9822
10801
|
credential: { type: "string" }
|
|
@@ -9844,7 +10823,7 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9844
10823
|
}),
|
|
9845
10824
|
defineCommand({
|
|
9846
10825
|
name: "refresh",
|
|
9847
|
-
summary: "Check a repository against
|
|
10826
|
+
summary: "Check a repository against its provider again.",
|
|
9848
10827
|
usage: "repositories refresh <owner/name> --project <name>",
|
|
9849
10828
|
optionsUsage: projectUsage,
|
|
9850
10829
|
options: projectOption,
|
|
@@ -9894,26 +10873,26 @@ const createRepositoriesCommand = (adaptersOf) => defineGroup({
|
|
|
9894
10873
|
});
|
|
9895
10874
|
//#endregion
|
|
9896
10875
|
//#region src/mcp/tools/repositories.ts
|
|
9897
|
-
const repositoryField = z.string().trim().min(1).describe("The repository, as owner/name, by its name or by its id.");
|
|
9898
|
-
const credentialField = z.string().trim().min(1).describe("A
|
|
10876
|
+
const repositoryField = z.string().trim().min(1).describe("The repository, as owner/name (group/subgroup/name on GitLab), by its name or by its id.");
|
|
10877
|
+
const credentialField = z.string().trim().min(1).describe("A git provider credential of the project (github_pat, gitlab_token or bitbucket_token), by its label or its id.");
|
|
9899
10878
|
const repositoryTools = [
|
|
9900
10879
|
defineTool({
|
|
9901
10880
|
name: "repositories_list",
|
|
9902
10881
|
title: "List repositories",
|
|
9903
|
-
description: "registered lists the
|
|
9904
|
-
operations: ["listRepositories", "
|
|
10882
|
+
description: "registered lists the repositories of a project, each with its provider and server; available lists what one git provider credential can see on its server, to pick one for repository_add.",
|
|
10883
|
+
operations: ["listRepositories", "listAvailableRepositories"],
|
|
9905
10884
|
safety: "read",
|
|
9906
10885
|
input: z.object({
|
|
9907
10886
|
project: projectField,
|
|
9908
|
-
source: z.enum(["registered", "
|
|
9909
|
-
credential: credentialField.optional().describe("The credential to look through, for
|
|
10887
|
+
source: z.enum(["registered", "available"]).default("registered"),
|
|
10888
|
+
credential: credentialField.optional().describe("The credential to look through, for available.")
|
|
9910
10889
|
}),
|
|
9911
10890
|
run: async ({ project, source, credential }, run) => {
|
|
9912
10891
|
const projectId = await projectIdOf(run, project);
|
|
9913
10892
|
if (source === "registered") return repositoriesOf(run.api, projectId);
|
|
9914
|
-
if (credential === void 0) throw new UsageError("
|
|
10893
|
+
if (credential === void 0) throw new UsageError("available needs a credential.");
|
|
9915
10894
|
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
9916
|
-
return dataOf("list the repositories of the credential", await run.api.GET("/projects/{id}/repositories/
|
|
10895
|
+
return dataOf("list the repositories of the credential", await run.api.GET("/projects/{id}/repositories/available", { params: {
|
|
9917
10896
|
path: { id: projectId },
|
|
9918
10897
|
query: { credentialId }
|
|
9919
10898
|
} }), repositoryHints);
|
|
@@ -9922,12 +10901,12 @@ const repositoryTools = [
|
|
|
9922
10901
|
defineTool({
|
|
9923
10902
|
name: "repository_add",
|
|
9924
10903
|
title: "Register a repository",
|
|
9925
|
-
description: "Register a
|
|
10904
|
+
description: "Register a repository of a git provider in a project, so agents can clone it. The credential decides the provider and the server.",
|
|
9926
10905
|
operations: ["registerRepository"],
|
|
9927
10906
|
safety: "write",
|
|
9928
10907
|
input: z.object({
|
|
9929
10908
|
project: projectField,
|
|
9930
|
-
repository: z.string().trim().min(1).describe("The
|
|
10909
|
+
repository: z.string().trim().min(1).describe("The repository, as owner/name, or group/subgroup/name for a GitLab subgroup."),
|
|
9931
10910
|
credential: credentialField
|
|
9932
10911
|
}),
|
|
9933
10912
|
run: async ({ project, repository, credential }, run) => {
|
|
@@ -9947,7 +10926,7 @@ const repositoryTools = [
|
|
|
9947
10926
|
defineTool({
|
|
9948
10927
|
name: "repository_update",
|
|
9949
10928
|
title: "Change or refresh a repository",
|
|
9950
|
-
description: "set_credential points a repository at another
|
|
10929
|
+
description: "set_credential points a repository at another credential of its provider and server; refresh checks it against its provider again.",
|
|
9951
10930
|
operations: ["updateRepository", "refreshRepository"],
|
|
9952
10931
|
safety: "write",
|
|
9953
10932
|
input: z.object({
|
|
@@ -10123,6 +11102,7 @@ const kcloudTools = [
|
|
|
10123
11102
|
...accountTools,
|
|
10124
11103
|
...organizationTools,
|
|
10125
11104
|
...projectTools,
|
|
11105
|
+
...claudeAccountTools,
|
|
10126
11106
|
...credentialTools,
|
|
10127
11107
|
...repositoryTools,
|
|
10128
11108
|
...projectToolTools,
|
|
@@ -10906,6 +11886,7 @@ const kcloudCommands = () => [
|
|
|
10906
11886
|
createTokensCommand(createAdapters),
|
|
10907
11887
|
createAuthCommand(createAdapters),
|
|
10908
11888
|
createProjectsCommand(createAdapters),
|
|
11889
|
+
createClaudeAccountsCommand(createAdapters),
|
|
10909
11890
|
createCredentialsCommand(createAdapters),
|
|
10910
11891
|
createRepositoriesCommand(createAdapters),
|
|
10911
11892
|
createToolsCommand(createAdapters),
|