@keystrokehq/cli 0.1.25 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +170 -101
- package/dist/index.mjs.map +1 -1
- package/dist/{maybe-auto-update-q5MthdI8.mjs → maybe-auto-update-BDvSKDZp.mjs} +2 -2
- package/dist/{maybe-auto-update-q5MthdI8.mjs.map → maybe-auto-update-BDvSKDZp.mjs.map} +1 -1
- package/dist/skills-bundle/_AGENTS.mcp.md +1 -1
- package/dist/skills-bundle/_AGENTS.md +215 -114
- package/dist/templates/hello-world/README.md +3 -2
- package/dist/{version-DcR3O1UD.mjs → version-pY9N8XlL.mjs} +1 -2
- package/dist/version-pY9N8XlL.mjs.map +1 -0
- package/package.json +3 -3
- package/dist/version-DcR3O1UD.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { $ as GetCustomAppResponseSchema, $n as slugifyAppName, $t as SlugAvailabilityResponseSchema, A as CreateCustomAppRequestSchema, An as WorkflowRunHooksResponseSchema, At as PollRunResponseSchema, B as CredentialConsumerListQuerySchema, Bt as ProjectReachabilityResponseSchema, C as ConnectAuthorizeUrlResponseSchema, Cn as UploadProjectSourceResponseSchema, Ct as ListProjectsResponseSchema, D as CreateCredentialInstanceBodySchema, Dn as UserPreferencesPatchSchema, Dt as OrganizationSidebarBrandingSchema, E as CreateApiKeyResponseSchema, En as UserAvatarSchema, Et as OrganizationSidebarBrandingPatchSchema, F as CreateProjectRequestSchema, Fn as WorkspaceTriggerFileSchema, Ft as PresignProjectSourceRequestSchema, G as DOCS_QUERY_TOOL, Gt as PromptResponseSchema, H as CredentialInstanceListResponseSchema, Hn as detectProjectPackageManagerFromSnapshot, Ht as ProjectSettingsResponseSchema, I as CreateProjectResponseSchema, In as WorkspaceTriggerListResponseSchema, It as PresignProjectSourceResponseSchema, J as DownloadActiveProjectArtifactResponseSchema, Jn as parseErrorResponse, Jt as QueuedRunResponseSchema, K as DOCS_SEARCH_TOOL, L as CredentialAssignmentListQuerySchema, Ln as WorkspaceTriggerOverviewSchema, Lt as PresignUserAvatarRequestSchema, M as CreateOrganizationRequestSchema, Mn as WorkflowSummaryDetailResponseSchema, Mt as PresignChatAttachmentResponseSchema, N as CreateOrganizationResponseSchema, Nn as WorkflowSummaryListResponseSchema, Nt as PresignOrgLogoRequestSchema, O as CreateCredentialsRequestSchema, On as UserPreferencesSchema, Ot as PROJECT_PULL_STATE_RELATIVE_PATH, P as CreateProjectArtifactResponseSchema, Pn as WorkspaceTriggerDetailSchema, Pt as PresignOrgLogoResponseSchema, Q as GetCredentialResponseSchema, Qn as resolvePublicPlatformOrigin, Qt as SkillSummaryListResponseSchema, R as CredentialAssignmentListResponseSchema, Rn as WorkspaceTriggerRunListResponseSchema, Rt as PresignUserAvatarResponseSchema, S as CompleteProjectArtifactResponseSchema, Sn as UploadProjectSourceManifestRequestSchema, St as ListProjectMetricsResponseSchema, T as CreateApiKeyRequestSchema, Tn as UserAvatarPatchSchema, Tt as OpenApiDiscoverResponseSchema, U as CredentialInstanceRecordSchema, Un as isAcceptableInstallExit, Ut as ProjectSlugAvailabilityResponseSchema, V as CredentialConsumerListResponseSchema, Vn as deriveCustomAppDisplay, Vt as ProjectResponseSchema, Wn as listenPortFromPublicUrl, Wt as PromptInputSchema, X as ErrorResponseSchema, Xn as resolveConnectAppSlug, Xt as RecentResourceListResponseSchema, Y as DownloadActiveProjectSourceResponseSchema, Z as GatewayAttachmentRecordSchema, Zn as resolveDocsMcpUrl, Zt as SkillSummaryDetailResponseSchema, _ as ChannelAccountListResponseSchema, _n as UpdateOrganizationRequestSchema, _t as ListOrganizationMembersResponseSchema, a as AgentSessionDetailResponseSchema, an as StartOAuthConnectionResultSchema, at as HistoryRunListResponseSchema, b as ChannelDirectoryListResponseSchema, bn as UpdateProjectRequestSchema, bt as ListProjectFilesResponseSchema, c as AgentSummaryListResponseSchema, cn as TriggerDetailResponseSchema, ct as InviteProjectMembersRequestSchema, d as AssignCredentialBodySchema, dn as TriggerRunListResponseSchema, dt as ListAgentMemoryFilesResponseSchema, en as StartKeystrokeConnectionInputSchema, et as GraphqlDiscoverResponseSchema, f as BindChannelBodySchema, fn as UpdateChannelBindingBodySchema, ft as ListAgentWorkspaceFilesResponseSchema, g as CatalogAppsPageResponseSchema, gn as UpdateOrganizationMemberResponseSchema, gt as ListOrganizationInvitationsResponseSchema, h as CatalogAppDetailResponseSchema, hn as UpdateOrganizationMemberRequestSchema, ht as ListCredentialsResponseSchema, i as AgentSessionChatStateResponseSchema, in as StartOAuthConnectionInputSchema, it as HistoryRunListQuerySchema, j as CreateCustomAppResponseSchema, jn as WorkflowRunListResponseSchema, jt as PresignChatAttachmentRequestSchema, k as CreateCredentialsResponseSchema, kn as WorkflowRunDetailResponseSchema, kt as PROJECT_REACHABILITY_REQUEST_TIMEOUT_MS, l as AgentTriggerSummaryListResponseSchema, ln as TriggerListResponseSchema, lt as InviteProjectMembersResponseSchema, m as CatalogActionsPageResponseSchema, mn as UpdateCredentialRequestSchema, mt as ListAppsResponseSchema, n as AcceptOrganizationInvitationResponseSchema, nn as StartMcpOAuthConnectionInputSchema, nt as HistoryRunCancelResponseSchema, o as AgentSessionListResponseSchema, on as SubmitMarketingContactRequestSchema, ot as InviteOrganizationMembersRequestSchema, p as CatalogActionDetailResponseSchema, pn as UpdateCredentialInstanceBodySchema, pt as ListApiKeysResponseSchema, q as DeclineOrganizationInvitationResponseSchema, qn as parseAppSlug, qt as QueuedAgentPromptResponseSchema, r as ActiveOrganizationResponseSchema, rn as StartMcpOAuthConnectionResultSchema, rt as HistoryRunDetailResponseSchema, s as AgentSummaryDetailResponseSchema, sn as SubmitTeamRequestRequestSchema, st as InviteOrganizationMembersResponseSchema, t as ACTIVE_ORG_HEADER, tn as StartKeystrokeConnectionResultSchema, tt as HealthResponseSchema, u as AppSlugAvailabilityResponseSchema, un as TriggerRunDetailResponseSchema, v as ChannelConnectionListResponseSchema, vn as UpdateProjectMemberRequestSchema, vt as ListOrganizationsResponseSchema, w as ConnectProvidersResponseSchema, wn as UpsertGatewayAttachmentBodySchema, wt as McpDiscoverResponseSchema, x as ChannelPlatformSchema, xn as UpdateProjectSettingsRequestSchema, xt as ListProjectMembersResponseSchema, y as ChannelConnectionSchema, yn as UpdateProjectMemberResponseSchema, yt as ListProjectDeploymentsResponseSchema, z as CredentialAssignmentRecordSchema, zn as buildConnectDeeplink, zt as ProjectPullStateSchema } from "./dist-BOhrc_Nv.mjs";
|
|
3
3
|
import { i as packProjectArtifact, n as withMcpReadClient, r as mergeFilteredArtifact, t as mapInParallelBatches } from "./dist-Re6HHSqz.mjs";
|
|
4
|
-
import { a as installPlaygroundDependencies, c as createCliConfig, d as getEffectiveApiTarget, f as getPlatformUrl, g as resolvePlatformUrlForWebUrl, i as installDependencies$1, l as getCliConfigDir, m as DEFAULT_PLATFORM_URL, n as buildPlaygroundWorkspace, o as resolvePackageManager, p as getWebUrl, s as resolveCliRoot, t as readCliVersion, u as getConfigDir } from "./version-
|
|
4
|
+
import { a as installPlaygroundDependencies, c as createCliConfig, d as getEffectiveApiTarget, f as getPlatformUrl, g as resolvePlatformUrlForWebUrl, i as installDependencies$1, l as getCliConfigDir, m as DEFAULT_PLATFORM_URL, n as buildPlaygroundWorkspace, o as resolvePackageManager, p as getWebUrl, s as resolveCliRoot, t as readCliVersion, u as getConfigDir } from "./version-pY9N8XlL.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -4684,7 +4684,7 @@ async function resolveProjectRef(platform, ref) {
|
|
|
4684
4684
|
if (looksLikeUuid(ref)) try {
|
|
4685
4685
|
return await platform.projects.get(ref);
|
|
4686
4686
|
} catch (error) {
|
|
4687
|
-
if (error instanceof PlatformError && error.status === 404) throw new Error(`Project ${ref} not found in the active organization. Run \`keystroke project list\`, then \`keystroke
|
|
4687
|
+
if (error instanceof PlatformError && error.status === 404) throw new Error(`Project ${ref} not found in the active organization. Run \`keystroke project list\`, then \`keystroke project link --project <slug>\`.`);
|
|
4688
4688
|
throw error;
|
|
4689
4689
|
}
|
|
4690
4690
|
const match = (await platform.projects.list()).find((project) => project.slug === ref);
|
|
@@ -4846,6 +4846,78 @@ function readDevSession(configDir = getCliConfigDir()) {
|
|
|
4846
4846
|
}
|
|
4847
4847
|
}
|
|
4848
4848
|
//#endregion
|
|
4849
|
+
//#region src/project/resolve-keystroke-config-root.ts
|
|
4850
|
+
const KEYSTROKE_CONFIG$2 = "keystroke.config.ts";
|
|
4851
|
+
/** Walk up from `fromDir` and return the directory containing `keystroke.config.ts`, if any. */
|
|
4852
|
+
function resolveKeystrokeConfigRoot(fromDir = process.cwd()) {
|
|
4853
|
+
let dir = resolve(fromDir);
|
|
4854
|
+
while (dir !== dirname(dir)) {
|
|
4855
|
+
if (existsSync(join(dir, KEYSTROKE_CONFIG$2))) return dir;
|
|
4856
|
+
dir = dirname(dir);
|
|
4857
|
+
}
|
|
4858
|
+
if (existsSync(join(dir, KEYSTROKE_CONFIG$2))) return dir;
|
|
4859
|
+
return null;
|
|
4860
|
+
}
|
|
4861
|
+
//#endregion
|
|
4862
|
+
//#region src/project/read-project-config.ts
|
|
4863
|
+
const KEYSTROKE_CONFIG$1 = "keystroke.config.ts";
|
|
4864
|
+
/** Remove line and block comments before scanning config literals. */
|
|
4865
|
+
function stripConfigComments(source) {
|
|
4866
|
+
return source.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
4867
|
+
}
|
|
4868
|
+
/** Mask comments while preserving string length so match indices stay aligned. */
|
|
4869
|
+
function stripConfigCommentsPreservingLength(source) {
|
|
4870
|
+
return source.replace(/\/\/[^\n]*/g, (match) => " ".repeat(match.length)).replace(/\/\*[\s\S]*?\*\//g, (match) => " ".repeat(match.length));
|
|
4871
|
+
}
|
|
4872
|
+
function readStringLiteral(source, key) {
|
|
4873
|
+
const stripped = stripConfigComments(source);
|
|
4874
|
+
const pattern = new RegExp(`\\b${key}\\s*:\\s*(['"])([^'"]*)\\1`);
|
|
4875
|
+
return stripped.match(pattern)?.[2]?.trim() || void 0;
|
|
4876
|
+
}
|
|
4877
|
+
function parseLinkedProjectConfig(source) {
|
|
4878
|
+
const project = readStringLiteral(source, "project");
|
|
4879
|
+
const organization = readStringLiteral(source, "organization");
|
|
4880
|
+
return {
|
|
4881
|
+
...project ? { project } : {},
|
|
4882
|
+
...organization ? { organization } : {}
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
/** Read optional project/organization slugs from keystroke.config.ts without executing it. */
|
|
4886
|
+
function readLinkedProjectConfig(fromDir = process.cwd()) {
|
|
4887
|
+
const root = resolveKeystrokeConfigRoot(fromDir);
|
|
4888
|
+
if (!root) return {};
|
|
4889
|
+
return parseLinkedProjectConfig(readFileSync(join(root, KEYSTROKE_CONFIG$1), "utf8"));
|
|
4890
|
+
}
|
|
4891
|
+
//#endregion
|
|
4892
|
+
//#region src/target-options.ts
|
|
4893
|
+
let targetOptions = {};
|
|
4894
|
+
function setCliTargetOptions(options) {
|
|
4895
|
+
targetOptions = options;
|
|
4896
|
+
}
|
|
4897
|
+
function getCliTargetOptions() {
|
|
4898
|
+
return targetOptions;
|
|
4899
|
+
}
|
|
4900
|
+
//#endregion
|
|
4901
|
+
//#region src/resolve-project-target.ts
|
|
4902
|
+
function resolveLinkedProjectRef(fromDir = process.cwd()) {
|
|
4903
|
+
return readLinkedProjectConfig(fromDir).project;
|
|
4904
|
+
}
|
|
4905
|
+
/** Activate the organization slug from keystroke.config.ts when present. */
|
|
4906
|
+
async function ensureLinkedOrganization(config, fromDir = process.cwd()) {
|
|
4907
|
+
const linked = readLinkedProjectConfig(fromDir);
|
|
4908
|
+
if (!linked.organization) return;
|
|
4909
|
+
await activateOrganization(config, resolveOrganizationRef(await listOrganizations(config), linked.organization).organization.id);
|
|
4910
|
+
}
|
|
4911
|
+
async function resolveActiveProject(config, platform, fromDir = process.cwd()) {
|
|
4912
|
+
const ref = getCliTargetOptions().projectId ?? readLinkedProjectConfig(fromDir).project;
|
|
4913
|
+
if (!ref) return;
|
|
4914
|
+
await ensureLinkedOrganization(config, fromDir);
|
|
4915
|
+
return resolveProjectRef(platform ?? createCliPlatformClient(config), ref);
|
|
4916
|
+
}
|
|
4917
|
+
async function resolveActiveProjectId(config, fromDir = process.cwd()) {
|
|
4918
|
+
return (await resolveActiveProject(config, void 0, fromDir))?.id;
|
|
4919
|
+
}
|
|
4920
|
+
//#endregion
|
|
4849
4921
|
//#region src/resolve-api-target.ts
|
|
4850
4922
|
/** Standalone local server default project id — keep in sync with @keystrokehq/database. */
|
|
4851
4923
|
const DEFAULT_LOCAL_PROJECT_ID = "default";
|
|
@@ -4878,6 +4950,7 @@ function resolveOrgPlatformTarget(config) {
|
|
|
4878
4950
|
};
|
|
4879
4951
|
}
|
|
4880
4952
|
async function resolvePlatformTarget(config, projectRef) {
|
|
4953
|
+
await ensureLinkedOrganization(config);
|
|
4881
4954
|
const platform = createCliPlatformClient(config);
|
|
4882
4955
|
let project;
|
|
4883
4956
|
try {
|
|
@@ -4905,20 +4978,11 @@ async function resolveApiTarget(config, options = {}) {
|
|
|
4905
4978
|
if (options.projectId) return resolvePlatformTarget(config, options.projectId);
|
|
4906
4979
|
if (getEffectiveApiTarget(config) === "local") return resolveLocalTarget(options);
|
|
4907
4980
|
if (!options.projectScoped) return resolveOrgPlatformTarget(config);
|
|
4908
|
-
const projectRef = options.projectId ??
|
|
4909
|
-
if (!projectRef) throw new Error("No project selected. Pass `--project <slug>` or run `keystroke
|
|
4981
|
+
const projectRef = options.projectId ?? readLinkedProjectConfig().project;
|
|
4982
|
+
if (!projectRef) throw new Error("No project selected. Pass `--project <slug>` or run `keystroke project link --project <slug>`.");
|
|
4910
4983
|
return resolvePlatformTarget(config, projectRef);
|
|
4911
4984
|
}
|
|
4912
4985
|
//#endregion
|
|
4913
|
-
//#region src/target-options.ts
|
|
4914
|
-
let targetOptions = {};
|
|
4915
|
-
function setCliTargetOptions(options) {
|
|
4916
|
-
targetOptions = options;
|
|
4917
|
-
}
|
|
4918
|
-
function getCliTargetOptions() {
|
|
4919
|
-
return targetOptions;
|
|
4920
|
-
}
|
|
4921
|
-
//#endregion
|
|
4922
4986
|
//#region src/errors/example-input.ts
|
|
4923
4987
|
function exampleValueFromValidationMessage(message) {
|
|
4924
4988
|
if (/expected number/i.test(message)) return 0;
|
|
@@ -5088,18 +5152,6 @@ async function requireAdminRole(config) {
|
|
|
5088
5152
|
if (!active || active.role !== "owner" && active.role !== "admin") throw new Error("--admin requires org owner or admin");
|
|
5089
5153
|
}
|
|
5090
5154
|
//#endregion
|
|
5091
|
-
//#region src/resolve-project-target.ts
|
|
5092
|
-
async function resolveActiveProject(config, platform) {
|
|
5093
|
-
const ref = getCliTargetOptions().projectId ?? config.get("activeProjectId");
|
|
5094
|
-
if (!ref) return;
|
|
5095
|
-
const client = platform ?? createCliPlatformClient(config);
|
|
5096
|
-
if (!getCliTargetOptions().projectId && looksLikeUuid(ref)) return resolveProjectRef(client, ref);
|
|
5097
|
-
return resolveProjectRef(client, ref);
|
|
5098
|
-
}
|
|
5099
|
-
async function resolveActiveProjectId(config) {
|
|
5100
|
-
return (await resolveActiveProject(config))?.id;
|
|
5101
|
-
}
|
|
5102
|
-
//#endregion
|
|
5103
5155
|
//#region src/commands/agent/list.ts
|
|
5104
5156
|
function registerAgentListCommand(agent) {
|
|
5105
5157
|
agent.command("list").description("List agents in the active organization").option("--admin", "List all org resources (org owner or admin only)").action((options) => runCliCommand("List agents failed", async () => {
|
|
@@ -5128,7 +5180,8 @@ function registerAgentPromptCommand(agent) {
|
|
|
5128
5180
|
}
|
|
5129
5181
|
//#endregion
|
|
5130
5182
|
//#region src/commands/project/run-project.ts
|
|
5131
|
-
async function withActivePlatformClient(config, fn) {
|
|
5183
|
+
async function withActivePlatformClient(config, fn, fromDir = process.cwd(), applyLinkedOrganization = true) {
|
|
5184
|
+
if (applyLinkedOrganization) await ensureLinkedOrganization(config, fromDir);
|
|
5132
5185
|
const platform = createCliPlatformClient(config);
|
|
5133
5186
|
let active = await platform.organizations.getActive();
|
|
5134
5187
|
if (!active) active = await selectActiveOrganization(config);
|
|
@@ -5139,7 +5192,7 @@ function writeInactiveDeployHints(projects) {
|
|
|
5139
5192
|
const bin = cliBinaryName();
|
|
5140
5193
|
for (const project of projects) {
|
|
5141
5194
|
if (project.status !== "inactive") continue;
|
|
5142
|
-
process.stderr.write(`Project ${formatProjectLabel(project)} is inactive. Deploy with: ${bin}
|
|
5195
|
+
process.stderr.write(`Project ${formatProjectLabel(project)} is inactive. Deploy with: ${bin} project link --project ${formatProjectLabel(project)}, then ${bin} deploy\n`);
|
|
5143
5196
|
}
|
|
5144
5197
|
}
|
|
5145
5198
|
async function runProjectList(config, options = {}) {
|
|
@@ -5543,19 +5596,6 @@ function registerAppShowCommand(app) {
|
|
|
5543
5596
|
}, void 0, { orgScoped: true }));
|
|
5544
5597
|
}
|
|
5545
5598
|
//#endregion
|
|
5546
|
-
//#region src/project/resolve-keystroke-config-root.ts
|
|
5547
|
-
const KEYSTROKE_CONFIG = "keystroke.config.ts";
|
|
5548
|
-
/** Walk up from `fromDir` and return the directory containing `keystroke.config.ts`, if any. */
|
|
5549
|
-
function resolveKeystrokeConfigRoot(fromDir = process.cwd()) {
|
|
5550
|
-
let dir = resolve(fromDir);
|
|
5551
|
-
while (dir !== dirname(dir)) {
|
|
5552
|
-
if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
|
|
5553
|
-
dir = dirname(dir);
|
|
5554
|
-
}
|
|
5555
|
-
if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
|
|
5556
|
-
return null;
|
|
5557
|
-
}
|
|
5558
|
-
//#endregion
|
|
5559
5599
|
//#region src/commands/app/generate-app-stub.ts
|
|
5560
5600
|
function slugPartToExportName(part) {
|
|
5561
5601
|
return part.split(/[-_]+/).filter(Boolean).map((segment, index) => index === 0 ? segment.charAt(0).toLowerCase() + segment.slice(1) : segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
|
|
@@ -6314,6 +6354,7 @@ async function runDeploy(options) {
|
|
|
6314
6354
|
const root = resolveProjectRoot(options.dir);
|
|
6315
6355
|
const config = createCliConfig();
|
|
6316
6356
|
const client = createCliPlatformClient(config);
|
|
6357
|
+
await ensureLinkedOrganization(config, root);
|
|
6317
6358
|
let active = await client.organizations.getActive();
|
|
6318
6359
|
if (!active) active = await selectActiveOrganization(config);
|
|
6319
6360
|
client.setActiveOrganizationId(active.organization.id);
|
|
@@ -6338,7 +6379,6 @@ async function runDeploy(options) {
|
|
|
6338
6379
|
while (Date.now() < deadline) {
|
|
6339
6380
|
const current = await client.projects.get(project.id);
|
|
6340
6381
|
if (current.status === "active") {
|
|
6341
|
-
config.set("activeProjectId", project.id);
|
|
6342
6382
|
config.set("apiTarget", "platform");
|
|
6343
6383
|
process.stdout.write(`Project active${current.baseUrl ? `: ${current.baseUrl}` : ""}\n`);
|
|
6344
6384
|
return;
|
|
@@ -6351,8 +6391,8 @@ async function runDeploy(options) {
|
|
|
6351
6391
|
function registerDeployCommand(program) {
|
|
6352
6392
|
program.command("deploy").description("Build, upload, and deploy the project to the platform").option("--dir <path>", "Project directory", process.cwd()).option("--filter <entry>", "Build and deploy only matching module entry keys (repeatable)", (value, previous) => [...previous, value], []).action(async (options) => {
|
|
6353
6393
|
try {
|
|
6354
|
-
const projectId = await resolveActiveProjectId(createCliConfig());
|
|
6355
|
-
if (!projectId) throw new Error("Usage: keystroke deploy --project <slug>");
|
|
6394
|
+
const projectId = await resolveActiveProjectId(createCliConfig(), resolveProjectRoot(options.dir));
|
|
6395
|
+
if (!projectId) throw new Error("Usage: keystroke deploy --project <slug> (or run `keystroke project link --project <slug>`)");
|
|
6356
6396
|
await runDeploy({
|
|
6357
6397
|
dir: options.dir,
|
|
6358
6398
|
projectId,
|
|
@@ -6773,7 +6813,7 @@ const INIT_CATALOG_VERSIONS = {
|
|
|
6773
6813
|
vitest: "^4.1.7",
|
|
6774
6814
|
"@types/node": "^25.9.1"
|
|
6775
6815
|
};
|
|
6776
|
-
const INIT_KEYSTROKE_VERSION = "^0.1.
|
|
6816
|
+
const INIT_KEYSTROKE_VERSION = "^0.1.23";
|
|
6777
6817
|
//#endregion
|
|
6778
6818
|
//#region src/init/copy-template.ts
|
|
6779
6819
|
function renderTemplate(content, variables) {
|
|
@@ -6916,6 +6956,39 @@ async function scaffoldAgentsGuide(projectRoot, fromModuleUrl = import.meta.url)
|
|
|
6916
6956
|
await writeBundleVersion(projectRoot);
|
|
6917
6957
|
}
|
|
6918
6958
|
//#endregion
|
|
6959
|
+
//#region src/project/write-project-config.ts
|
|
6960
|
+
const KEYSTROKE_CONFIG = "keystroke.config.ts";
|
|
6961
|
+
function upsertStringLiteral(source, key, value) {
|
|
6962
|
+
const stripped = stripConfigCommentsPreservingLength(source);
|
|
6963
|
+
const entryPattern = new RegExp(`\\b${key}\\s*:\\s*(['"])[^'"]*\\1,?\\s*\\n?`);
|
|
6964
|
+
const entry = value === void 0 ? void 0 : ` ${key}: "${value}",\n`;
|
|
6965
|
+
const match = stripped.match(entryPattern);
|
|
6966
|
+
if (match?.index !== void 0) {
|
|
6967
|
+
const start = match.index;
|
|
6968
|
+
const end = start + match[0].length;
|
|
6969
|
+
if (value === void 0) return source.slice(0, start) + source.slice(end);
|
|
6970
|
+
return source.slice(0, start) + entry + source.slice(end);
|
|
6971
|
+
}
|
|
6972
|
+
if (value === void 0) return source;
|
|
6973
|
+
const defineConfigPattern = /defineConfig\(\{\s*\n?/;
|
|
6974
|
+
if (defineConfigPattern.test(source)) return source.replace(defineConfigPattern, `defineConfig({\n${entry}`);
|
|
6975
|
+
throw new Error(`Could not find defineConfig({ ... }) in ${KEYSTROKE_CONFIG}`);
|
|
6976
|
+
}
|
|
6977
|
+
function upsertLinkedProjectConfig(source, input) {
|
|
6978
|
+
let next = source;
|
|
6979
|
+
if (input.project !== void 0) next = upsertStringLiteral(next, "project", input.project);
|
|
6980
|
+
if (input.organization !== void 0) next = upsertStringLiteral(next, "organization", input.organization);
|
|
6981
|
+
return next;
|
|
6982
|
+
}
|
|
6983
|
+
/** Write project/organization slugs into keystroke.config.ts. */
|
|
6984
|
+
function writeLinkedProjectConfig(input, fromDir = process.cwd()) {
|
|
6985
|
+
const root = resolveKeystrokeConfigRoot(fromDir);
|
|
6986
|
+
if (!root) throw new Error(`No ${KEYSTROKE_CONFIG} found — run from a keystroke project directory`);
|
|
6987
|
+
const configPath = join(root, KEYSTROKE_CONFIG);
|
|
6988
|
+
writeFileSync(configPath, upsertLinkedProjectConfig(readFileSync(configPath, "utf8"), input));
|
|
6989
|
+
return configPath;
|
|
6990
|
+
}
|
|
6991
|
+
//#endregion
|
|
6919
6992
|
//#region src/init/scaffold-empty-src-dirs.ts
|
|
6920
6993
|
const EMPTY_SRC_DIRS = [
|
|
6921
6994
|
"files",
|
|
@@ -7016,6 +7089,10 @@ async function runInit(options) {
|
|
|
7016
7089
|
else await scaffoldProjectDotfiles(targetDir);
|
|
7017
7090
|
await scaffoldAgentsGuide(targetDir);
|
|
7018
7091
|
await copyFile(join(targetDir, ".env.example"), join(targetDir, ".env"));
|
|
7092
|
+
if (options.project || options.organization) writeLinkedProjectConfig({
|
|
7093
|
+
...options.project ? { project: options.project } : {},
|
|
7094
|
+
...options.organization ? { organization: options.organization } : {}
|
|
7095
|
+
}, targetDir);
|
|
7019
7096
|
if (!options.skipInstall) if (playgroundRoot) {
|
|
7020
7097
|
installPlaygroundDependencies(targetDir);
|
|
7021
7098
|
buildPlaygroundWorkspace(playgroundRoot);
|
|
@@ -7028,7 +7105,7 @@ async function runInit(options) {
|
|
|
7028
7105
|
//#endregion
|
|
7029
7106
|
//#region src/commands/init.ts
|
|
7030
7107
|
function registerInitCommand(program) {
|
|
7031
|
-
program.command("init").description("Create a new keystroke project from a template").argument("[directory]", "Directory to create the project in").option("--name <name>", "Project name (kebab-case)").option("--template <name>", "Template to use", "hello-world").option("-y, --yes", "Skip prompts (headless mode for scripts and AI agents)").option("--skip-install", "Skip dependency installation").option("--pm <manager>", "Package manager: npm, pnpm, yarn, or bun").option("--no-example", "Scaffold without the example action, agent, and workflow").action(async (directory, options) => {
|
|
7108
|
+
program.command("init").description("Create a new keystroke project from a template").argument("[directory]", "Directory to create the project in").option("--name <name>", "Project name (kebab-case)").option("--template <name>", "Template to use", "hello-world").option("-y, --yes", "Skip prompts (headless mode for scripts and AI agents)").option("--skip-install", "Skip dependency installation").option("--pm <manager>", "Package manager: npm, pnpm, yarn, or bun").option("--no-example", "Scaffold without the example action, agent, and workflow").option("--project <slug>", "Platform project slug to write into keystroke.config.ts").option("--organization <slug>", "Platform organization slug to write into keystroke.config.ts").action(async (directory, options) => {
|
|
7032
7109
|
try {
|
|
7033
7110
|
const result = await runInit({
|
|
7034
7111
|
directory,
|
|
@@ -7038,14 +7115,16 @@ function registerInitCommand(program) {
|
|
|
7038
7115
|
skipInstall: options.skipInstall,
|
|
7039
7116
|
packageManager: options.pm,
|
|
7040
7117
|
noExample: options.example === false,
|
|
7118
|
+
project: options.project,
|
|
7119
|
+
organization: options.organization,
|
|
7041
7120
|
version: readCliVersion()
|
|
7042
7121
|
});
|
|
7043
7122
|
process.stdout.write(`Created keystroke project "${result.projectName}" at ${result.targetDir}\n`);
|
|
7044
7123
|
process.stdout.write("\nNext steps:\n");
|
|
7045
7124
|
process.stdout.write(` cd ${result.targetDir}\n`);
|
|
7046
7125
|
process.stdout.write(" keystroke auth login # once\n");
|
|
7047
|
-
process.stdout.write(" keystroke project
|
|
7048
|
-
process.stdout.write(" keystroke deploy
|
|
7126
|
+
if (!options.project) process.stdout.write(" keystroke project link --project <slug> # link this directory to a platform project\n");
|
|
7127
|
+
process.stdout.write(" keystroke deploy # build + ship src/ to the platform\n");
|
|
7049
7128
|
} catch (error) {
|
|
7050
7129
|
const message = error instanceof Error ? error.message : "Init failed";
|
|
7051
7130
|
process.stderr.write(`${message}\n`);
|
|
@@ -7054,15 +7133,6 @@ function registerInitCommand(program) {
|
|
|
7054
7133
|
});
|
|
7055
7134
|
}
|
|
7056
7135
|
//#endregion
|
|
7057
|
-
//#region src/active-project.ts
|
|
7058
|
-
function clearActiveProjectIfMatches(config, project) {
|
|
7059
|
-
const activeProjectId = config.get("activeProjectId");
|
|
7060
|
-
if (activeProjectId === project.id || activeProjectId === project.slug) config.delete("activeProjectId");
|
|
7061
|
-
}
|
|
7062
|
-
function clearActiveProject(config) {
|
|
7063
|
-
if (config.get("activeProjectId") !== void 0) config.delete("activeProjectId");
|
|
7064
|
-
}
|
|
7065
|
-
//#endregion
|
|
7066
7136
|
//#region src/commands/organization/run-organization.ts
|
|
7067
7137
|
async function runOrganizationUpdate(config, input) {
|
|
7068
7138
|
await withActivePlatformClient(config, async (platform) => {
|
|
@@ -7104,7 +7174,6 @@ async function runOrganizationLeave(config) {
|
|
|
7104
7174
|
await platform.members.removeOrganizationMember(user.id);
|
|
7105
7175
|
platform.setActiveOrganizationId(null);
|
|
7106
7176
|
config.delete("activeOrganizationId");
|
|
7107
|
-
clearActiveProject(config);
|
|
7108
7177
|
process.stdout.write(`${JSON.stringify({ left: user.id }, null, 2)}\n`);
|
|
7109
7178
|
});
|
|
7110
7179
|
}
|
|
@@ -7177,9 +7246,23 @@ function registerOrganizationCommand(program) {
|
|
|
7177
7246
|
});
|
|
7178
7247
|
}
|
|
7179
7248
|
//#endregion
|
|
7249
|
+
//#region src/commands/project/run-project-link.ts
|
|
7250
|
+
async function runProjectLink(config, projectRef, fromDir = process.cwd()) {
|
|
7251
|
+
await withActivePlatformClient(config, async (platform) => {
|
|
7252
|
+
const project = await resolveProjectRef(platform, projectRef);
|
|
7253
|
+
const membership = (await listOrganizations(config)).find((entry) => entry.organization.id === project.organizationId);
|
|
7254
|
+
if (!membership) throw new Error(`Organization for project ${formatProjectLabel(project)} was not found or you do not have access`);
|
|
7255
|
+
const configPath = writeLinkedProjectConfig({
|
|
7256
|
+
project: project.slug,
|
|
7257
|
+
organization: membership.organization.slug
|
|
7258
|
+
}, fromDir);
|
|
7259
|
+
process.stdout.write(`Linked ${configPath} to ${membership.organization.slug}/${formatProjectLabel(project)}\nDeploy with: ${cliBinaryName()} deploy\n`);
|
|
7260
|
+
}, fromDir, false);
|
|
7261
|
+
}
|
|
7262
|
+
//#endregion
|
|
7180
7263
|
//#region src/commands/project/run-project-members.ts
|
|
7181
7264
|
async function resolveMembersProject(platform, config, projectRef) {
|
|
7182
|
-
const ref = projectRef ?? getCliTargetOptions().projectId ??
|
|
7265
|
+
const ref = projectRef ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
|
|
7183
7266
|
if (!ref) throw new Error("Usage: keystroke --project <slug> project members <command>");
|
|
7184
7267
|
return resolveProjectRef(platform, ref);
|
|
7185
7268
|
}
|
|
@@ -7219,7 +7302,6 @@ async function runProjectMembersLeave(config, options) {
|
|
|
7219
7302
|
await withActivePlatformClient(config, async (platform) => {
|
|
7220
7303
|
const project = await resolveMembersProject(platform, config, options.projectRef);
|
|
7221
7304
|
await platform.members.leaveProject(project.id);
|
|
7222
|
-
clearActiveProjectIfMatches(config, project);
|
|
7223
7305
|
process.stdout.write(`${JSON.stringify({ left: project.id }, null, 2)}\n`);
|
|
7224
7306
|
});
|
|
7225
7307
|
}
|
|
@@ -7233,7 +7315,7 @@ function parseProjectRole$1(role) {
|
|
|
7233
7315
|
throw new Error("Role must be admin or builder");
|
|
7234
7316
|
}
|
|
7235
7317
|
function resolveProjectRefOption$1(config, optionsProject) {
|
|
7236
|
-
return optionsProject ?? getCliTargetOptions().projectId ??
|
|
7318
|
+
return optionsProject ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
|
|
7237
7319
|
}
|
|
7238
7320
|
function projectRefInput$1(config, optionsProject) {
|
|
7239
7321
|
const projectRef = resolveProjectRefOption$1(config, optionsProject);
|
|
@@ -7299,7 +7381,7 @@ function registerProjectMembersCommand(project) {
|
|
|
7299
7381
|
//#endregion
|
|
7300
7382
|
//#region src/commands/project/run-project-settings.ts
|
|
7301
7383
|
async function resolveSettingsProject(platform, config, projectRef) {
|
|
7302
|
-
const ref = projectRef ?? getCliTargetOptions().projectId ??
|
|
7384
|
+
const ref = projectRef ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
|
|
7303
7385
|
if (!ref) throw new Error("Usage: keystroke --project <slug> project settings <command>");
|
|
7304
7386
|
return resolveProjectRef(platform, ref);
|
|
7305
7387
|
}
|
|
@@ -7321,7 +7403,7 @@ async function runProjectSettingsUpdate(config, input) {
|
|
|
7321
7403
|
//#endregion
|
|
7322
7404
|
//#region src/commands/project/settings.ts
|
|
7323
7405
|
function resolveProjectRefOption(config, optionsProject) {
|
|
7324
|
-
return optionsProject ?? getCliTargetOptions().projectId ??
|
|
7406
|
+
return optionsProject ?? getCliTargetOptions().projectId ?? readLinkedProjectConfig().project;
|
|
7325
7407
|
}
|
|
7326
7408
|
function projectRefInput(config, optionsProject) {
|
|
7327
7409
|
const projectRef = resolveProjectRefOption(config, optionsProject);
|
|
@@ -7402,8 +7484,8 @@ function registerProjectCommand(program) {
|
|
|
7402
7484
|
project.command("deployments").description("View deployment history for platform projects").command("list").description("List deployment history for a project").option("--project <slug>", "Project slug").action(async (options) => {
|
|
7403
7485
|
const config = createCliConfig();
|
|
7404
7486
|
try {
|
|
7405
|
-
const projectRef = options.project ?? getCliTargetOptions().projectId ??
|
|
7406
|
-
if (!projectRef) throw new Error("Usage: keystroke project deployments list --project <slug>");
|
|
7487
|
+
const projectRef = options.project ?? getCliTargetOptions().projectId ?? resolveLinkedProjectRef();
|
|
7488
|
+
if (!projectRef) throw new Error("Usage: keystroke project deployments list --project <slug> (or run `keystroke project link`)");
|
|
7407
7489
|
await runProjectDeploymentsList(config, { projectRef });
|
|
7408
7490
|
} catch (error) {
|
|
7409
7491
|
process.stderr.write(`${formatCliError(error, "List deployments failed", {
|
|
@@ -7413,6 +7495,18 @@ function registerProjectCommand(program) {
|
|
|
7413
7495
|
process.exitCode = 1;
|
|
7414
7496
|
}
|
|
7415
7497
|
});
|
|
7498
|
+
project.command("link").description("Link this directory to a platform project in keystroke.config.ts").requiredOption("--project <slug>", "Platform project slug").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
|
|
7499
|
+
const config = createCliConfig();
|
|
7500
|
+
try {
|
|
7501
|
+
await runProjectLink(config, options.project, options.dir);
|
|
7502
|
+
} catch (error) {
|
|
7503
|
+
process.stderr.write(`${formatCliError(error, "Link project failed", {
|
|
7504
|
+
serverUrl: getPlatformUrl(config),
|
|
7505
|
+
webUrl: getWebUrl(config)
|
|
7506
|
+
})}\n`);
|
|
7507
|
+
process.exitCode = 1;
|
|
7508
|
+
}
|
|
7509
|
+
});
|
|
7416
7510
|
project.command("create").description("Create a platform project in the active organization").requiredOption("--name <name>", "Project name").option("--description <description>", "Project description").action(async (options) => {
|
|
7417
7511
|
const config = createCliConfig();
|
|
7418
7512
|
try {
|
|
@@ -7528,7 +7622,7 @@ async function runPull(options) {
|
|
|
7528
7622
|
function registerPullCommand(program) {
|
|
7529
7623
|
program.command("pull").description("Download the active deploy source snapshot into a local directory").option("--dir <path>", "Target directory", process.cwd()).option("--force", "Overwrite an existing local project tree").action(async (options) => runCliCommand("Pull failed", async () => {
|
|
7530
7624
|
const config = createCliConfig();
|
|
7531
|
-
const projectId = await resolveActiveProjectId(config);
|
|
7625
|
+
const projectId = await resolveActiveProjectId(config, resolveProjectRoot(options.dir));
|
|
7532
7626
|
if (!projectId) throw new Error("Usage: keystroke pull --project <slug>");
|
|
7533
7627
|
await runPull({
|
|
7534
7628
|
client: createCliPlatformClient(config),
|
|
@@ -7820,9 +7914,10 @@ function registerChannelPlatformsCommand(channel) {
|
|
|
7820
7914
|
//#region src/commands/channel/resolve-channel-project.ts
|
|
7821
7915
|
async function resolveChannelProjectId(config, projectRef) {
|
|
7822
7916
|
const platform = createCliPlatformClient(config);
|
|
7917
|
+
await ensureLinkedOrganization(config);
|
|
7823
7918
|
await ensureActiveOrganization(config);
|
|
7824
|
-
const ref = projectRef?.trim() || getCliTargetOptions().projectId?.trim() ||
|
|
7825
|
-
if (!ref) throw new Error("Project is required. Pass --project <slug> or
|
|
7919
|
+
const ref = projectRef?.trim() || getCliTargetOptions().projectId?.trim() || readLinkedProjectConfig().project?.trim();
|
|
7920
|
+
if (!ref) throw new Error("Project is required. Pass --project <slug> or run `keystroke project link --project <slug>`.");
|
|
7826
7921
|
return (await resolveProjectRef(platform, ref)).id;
|
|
7827
7922
|
}
|
|
7828
7923
|
//#endregion
|
|
@@ -7920,27 +8015,6 @@ function registerChannelCommand(program) {
|
|
|
7920
8015
|
registerChannelUpdateBindingCommand(channel);
|
|
7921
8016
|
}
|
|
7922
8017
|
//#endregion
|
|
7923
|
-
//#region src/commands/config/use-project.ts
|
|
7924
|
-
async function fetchProjectInOrg(config, projectRef) {
|
|
7925
|
-
return resolveProjectRef(createCliPlatformClient(config), projectRef);
|
|
7926
|
-
}
|
|
7927
|
-
function assertProjectReadyForConfig(project) {
|
|
7928
|
-
const label = formatProjectLabel(project);
|
|
7929
|
-
if (project.status === "failed") throw new Error(project.lastError ? `Project ${label} deploy failed: ${project.lastError}` : `Project ${label} deploy failed`);
|
|
7930
|
-
if (project.status === "starting") throw new Error(`Project ${label} is still starting. Wait for deploy to finish, then run \`keystroke config use project ${label}\`.`);
|
|
7931
|
-
}
|
|
7932
|
-
async function runConfigUseProject(config, projectRef) {
|
|
7933
|
-
const project = await fetchProjectInOrg(config, projectRef);
|
|
7934
|
-
assertProjectReadyForConfig(project);
|
|
7935
|
-
config.set("activeProjectId", project.id);
|
|
7936
|
-
config.set("apiTarget", "platform");
|
|
7937
|
-
const label = formatProjectLabel(project);
|
|
7938
|
-
return {
|
|
7939
|
-
stdout: project.status === "active" ? `Using platform project ${label}\n` : `Using platform project ${label} (${project.status})\n`,
|
|
7940
|
-
stderr: project.status === "inactive" ? `Project is not active yet. Deploy with: ${cliBinaryName()} deploy --project ${label}\n` : void 0
|
|
7941
|
-
};
|
|
7942
|
-
}
|
|
7943
|
-
//#endregion
|
|
7944
8018
|
//#region src/commands/config/preferences-patch.ts
|
|
7945
8019
|
function parsePreferenceValue(rawValue) {
|
|
7946
8020
|
try {
|
|
@@ -7994,22 +8068,23 @@ function registerConfigCommand(program) {
|
|
|
7994
8068
|
config.command("show").description("Show CLI configuration").action(() => {
|
|
7995
8069
|
const cliConfig = createCliConfig();
|
|
7996
8070
|
const configDir = getConfigDir(cliConfig);
|
|
8071
|
+
const linked = readLinkedProjectConfig();
|
|
7997
8072
|
process.stdout.write(`${JSON.stringify({
|
|
7998
8073
|
webUrl: cliConfig.get("webUrl"),
|
|
7999
8074
|
platformUrl: getPlatformUrl(cliConfig),
|
|
8000
8075
|
localApiUrl: resolveLocalApiOrigin(process.cwd()),
|
|
8001
8076
|
activeOrganizationId: cliConfig.get("activeOrganizationId"),
|
|
8002
|
-
|
|
8077
|
+
linkedProject: linked.project ?? null,
|
|
8078
|
+
linkedOrganization: linked.organization ?? null,
|
|
8003
8079
|
apiTarget: getEffectiveApiTarget(cliConfig),
|
|
8004
8080
|
devSession: readDevSession(configDir) ?? null
|
|
8005
8081
|
}, null, 2)}\n`);
|
|
8006
8082
|
});
|
|
8007
|
-
config.command("use").description("Switch API target (local/cloud
|
|
8083
|
+
config.command("use").description("Switch API target (local/cloud) or active organization").argument("[mode]", "local, cloud, or org").argument("[id]", "Organization slug when switching org").action(async (mode, id) => {
|
|
8008
8084
|
const cliConfig = createCliConfig();
|
|
8009
8085
|
if (!mode || mode === "local") {
|
|
8010
8086
|
cliConfig.set("apiTarget", "local");
|
|
8011
|
-
|
|
8012
|
-
process.stdout.write(activeProjectId ? `Using local keystroke server (cloud project ${activeProjectId} unchanged)\n` : "Using local keystroke server\n");
|
|
8087
|
+
process.stdout.write("Using local keystroke server\n");
|
|
8013
8088
|
return;
|
|
8014
8089
|
}
|
|
8015
8090
|
if (mode === "org") {
|
|
@@ -8019,16 +8094,10 @@ function registerConfigCommand(program) {
|
|
|
8019
8094
|
}
|
|
8020
8095
|
if (mode === "cloud") {
|
|
8021
8096
|
cliConfig.set("apiTarget", "platform");
|
|
8022
|
-
|
|
8023
|
-
process.stdout.write(activeProjectId ? `Using platform API (default project ${activeProjectId} for project-scoped commands)\n` : "Using platform API\n");
|
|
8097
|
+
process.stdout.write("Using platform API\n");
|
|
8024
8098
|
return;
|
|
8025
8099
|
}
|
|
8026
|
-
|
|
8027
|
-
const targetProjectId = id ?? cliConfig.get("activeProjectId");
|
|
8028
|
-
if (!targetProjectId) throw new Error("Usage: keystroke config use project <slug>");
|
|
8029
|
-
const result = await runConfigUseProject(cliConfig, targetProjectId);
|
|
8030
|
-
process.stdout.write(result.stdout);
|
|
8031
|
-
if (result.stderr) process.stderr.write(result.stderr);
|
|
8100
|
+
throw new Error("Usage: keystroke config use [local|cloud|org [slug]]");
|
|
8032
8101
|
});
|
|
8033
8102
|
config.command("org").description("List organizations you belong to").action(async () => {
|
|
8034
8103
|
const organizations = await listOrganizations(createCliConfig());
|
|
@@ -8138,7 +8207,7 @@ function createProgram() {
|
|
|
8138
8207
|
return program;
|
|
8139
8208
|
}
|
|
8140
8209
|
async function runCli(argv) {
|
|
8141
|
-
const { maybeAutoUpdate } = await import("./maybe-auto-update-
|
|
8210
|
+
const { maybeAutoUpdate } = await import("./maybe-auto-update-BDvSKDZp.mjs");
|
|
8142
8211
|
await maybeAutoUpdate(argv);
|
|
8143
8212
|
createProgram().parse(argv);
|
|
8144
8213
|
}
|