@keystrokehq/cli 0.0.109 → 0.0.111
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/{schemas-DwAYtN9C.mjs → discovery-XqwkB9H_-CQLSzR72.mjs} +20 -2
- package/dist/discovery-XqwkB9H_-CQLSzR72.mjs.map +1 -0
- package/dist/{dist-D1ip549D.mjs → dist-IKyaKHgU.mjs} +3 -20
- package/dist/dist-IKyaKHgU.mjs.map +1 -0
- package/dist/index.mjs +299 -170
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/dist-D1ip549D.mjs.map +0 -1
- package/dist/schemas-DwAYtN9C.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as array, c as discriminatedUnion, d as object, f as record, g as _coercedNumber, h as unknown, i as _enum, l as literal, m as union, n as ZodNumber, o as boolean, p as string, r as ZodType, s as custom, t as entryIdFromFile, u as number$1 } from "./discovery-XqwkB9H_-CQLSzR72.mjs";
|
|
3
3
|
import { a as installPlaygroundDependencies, i as installDependencies, n as buildPlaygroundWorkspace, o as resolvePackageManager, s as resolveCliRoot, t as readCliVersion } from "./version-DESgLEkE.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { Entry } from "@napi-rs/keyring";
|
|
7
7
|
import Conf from "conf";
|
|
8
8
|
import { homedir, platform, tmpdir } from "node:os";
|
|
9
|
-
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
9
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
10
10
|
import { confirm, input, select } from "@inquirer/prompts";
|
|
11
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { spawn, spawnSync } from "node:child_process";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
13
14
|
import { pathToFileURL } from "node:url";
|
|
14
15
|
import { access, copyFile, cp, mkdir, readFile, readdir, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
15
16
|
//#region ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/KyError.js
|
|
@@ -2063,13 +2064,13 @@ const OrganizationUserRoleSchema = _enum([
|
|
|
2063
2064
|
"admin",
|
|
2064
2065
|
"builder"
|
|
2065
2066
|
]);
|
|
2066
|
-
const isoDateTime$
|
|
2067
|
+
const isoDateTime$3 = string().datetime();
|
|
2067
2068
|
const OrganizationSchema = object({
|
|
2068
2069
|
id: string(),
|
|
2069
2070
|
name: string(),
|
|
2070
2071
|
slug: OrganizationSlugSchema,
|
|
2071
|
-
createdAt: isoDateTime$
|
|
2072
|
-
updatedAt: isoDateTime$
|
|
2072
|
+
createdAt: isoDateTime$3,
|
|
2073
|
+
updatedAt: isoDateTime$3
|
|
2073
2074
|
});
|
|
2074
2075
|
const ProjectSchema = object({
|
|
2075
2076
|
id: string(),
|
|
@@ -2080,8 +2081,8 @@ const ProjectSchema = object({
|
|
|
2080
2081
|
baseUrl: string().nullable(),
|
|
2081
2082
|
runtimeId: string().nullable(),
|
|
2082
2083
|
lastError: string().nullable(),
|
|
2083
|
-
createdAt: isoDateTime$
|
|
2084
|
-
updatedAt: isoDateTime$
|
|
2084
|
+
createdAt: isoDateTime$3,
|
|
2085
|
+
updatedAt: isoDateTime$3
|
|
2085
2086
|
});
|
|
2086
2087
|
const UserOrganizationSchema = object({
|
|
2087
2088
|
organization: OrganizationSchema,
|
|
@@ -2419,7 +2420,7 @@ const GatewayAttachmentSourceSchema = object({
|
|
|
2419
2420
|
channelIds: array(string()).optional(),
|
|
2420
2421
|
subscribeOnMention: boolean()
|
|
2421
2422
|
});
|
|
2422
|
-
|
|
2423
|
+
const GatewayAttachmentRecordSchema = object({
|
|
2423
2424
|
id: string(),
|
|
2424
2425
|
key: string(),
|
|
2425
2426
|
agentKey: string(),
|
|
@@ -2428,13 +2429,14 @@ object({ attachments: array(object({
|
|
|
2428
2429
|
source: GatewayAttachmentSourceSchema,
|
|
2429
2430
|
registeredAt: string(),
|
|
2430
2431
|
updatedAt: string()
|
|
2431
|
-
})
|
|
2432
|
-
object({
|
|
2432
|
+
});
|
|
2433
|
+
object({ attachments: array(GatewayAttachmentRecordSchema) });
|
|
2434
|
+
const UpsertGatewayAttachmentBodySchema = object({
|
|
2433
2435
|
agentKey: string().min(1),
|
|
2434
2436
|
mode: SlackGatewayModeSchema.optional(),
|
|
2435
2437
|
subscribeOnMention: boolean().optional()
|
|
2436
2438
|
});
|
|
2437
|
-
object({ agents: array(object({
|
|
2439
|
+
const AgentListResponseSchema = object({ agents: array(object({
|
|
2438
2440
|
key: string(),
|
|
2439
2441
|
name: string().optional(),
|
|
2440
2442
|
route: string()
|
|
@@ -2560,7 +2562,7 @@ const OrganizationMemberStatusSchema = _enum([
|
|
|
2560
2562
|
"suspended"
|
|
2561
2563
|
]);
|
|
2562
2564
|
const InvitableOrganizationMemberRoleSchema = _enum(["admin", "builder"]);
|
|
2563
|
-
const isoDateTime$
|
|
2565
|
+
const isoDateTime$2 = string().datetime();
|
|
2564
2566
|
const OrganizationMemberSchema = object({
|
|
2565
2567
|
id: string(),
|
|
2566
2568
|
name: string(),
|
|
@@ -2568,8 +2570,8 @@ const OrganizationMemberSchema = object({
|
|
|
2568
2570
|
role: OrganizationMemberRoleSchema,
|
|
2569
2571
|
status: OrganizationMemberStatusSchema,
|
|
2570
2572
|
avatarUrl: string().optional(),
|
|
2571
|
-
joinedAt: isoDateTime$
|
|
2572
|
-
lastActiveAt: isoDateTime$
|
|
2573
|
+
joinedAt: isoDateTime$2.optional(),
|
|
2574
|
+
lastActiveAt: isoDateTime$2.nullable().optional()
|
|
2573
2575
|
});
|
|
2574
2576
|
const ListOrganizationMembersResponseSchema = object({ members: array(OrganizationMemberSchema) });
|
|
2575
2577
|
const InviteOrganizationMembersRequestSchema = object({
|
|
@@ -2600,14 +2602,14 @@ const ListOrganizationInvitationsResponseSchema = object({ invitations: array(ob
|
|
|
2600
2602
|
const AcceptOrganizationInvitationResponseSchema = object({ organization: UserOrganizationSchema });
|
|
2601
2603
|
const DeclineOrganizationInvitationResponseSchema = object({ success: literal(true) });
|
|
2602
2604
|
const ProjectArtifactStatusSchema = _enum(["pending", "ready"]);
|
|
2603
|
-
const isoDateTime = string().datetime();
|
|
2605
|
+
const isoDateTime$1 = string().datetime();
|
|
2604
2606
|
const ProjectArtifactSchema = object({
|
|
2605
2607
|
id: string(),
|
|
2606
2608
|
projectId: string(),
|
|
2607
2609
|
storageKey: string(),
|
|
2608
2610
|
status: ProjectArtifactStatusSchema,
|
|
2609
|
-
createdAt: isoDateTime,
|
|
2610
|
-
updatedAt: isoDateTime
|
|
2611
|
+
createdAt: isoDateTime$1,
|
|
2612
|
+
updatedAt: isoDateTime$1
|
|
2611
2613
|
});
|
|
2612
2614
|
const CreateProjectArtifactResponseSchema = object({
|
|
2613
2615
|
artifact: ProjectArtifactSchema,
|
|
@@ -2618,6 +2620,76 @@ const CompleteProjectArtifactResponseSchema = object({
|
|
|
2618
2620
|
artifact: ProjectArtifactSchema,
|
|
2619
2621
|
project: ProjectSchema
|
|
2620
2622
|
});
|
|
2623
|
+
const isoDateTime = string().datetime();
|
|
2624
|
+
const sha256Hex = string().regex(/^[a-f0-9]{64}$/, "Expected a lowercase hex sha256 digest");
|
|
2625
|
+
/**
|
|
2626
|
+
* A single entry in a deploy's file tree, as served to the dashboard. Carries
|
|
2627
|
+
* no contents: `id` is a stable, path-derived handle used for selection and
|
|
2628
|
+
* `?file=` deep links; `hash` is the content address used to fetch the bytes
|
|
2629
|
+
* lazily and to cache them immutably in the browser.
|
|
2630
|
+
*/
|
|
2631
|
+
const ProjectFileSummarySchema = object({
|
|
2632
|
+
id: string(),
|
|
2633
|
+
path: string().min(1),
|
|
2634
|
+
hash: sha256Hex
|
|
2635
|
+
});
|
|
2636
|
+
/** Maps a runtime resource key (agent/workflow/skill) to its source file path. */
|
|
2637
|
+
const ProjectSourceResourceRefSchema = object({
|
|
2638
|
+
key: string().min(1),
|
|
2639
|
+
path: string().min(1)
|
|
2640
|
+
});
|
|
2641
|
+
const ProjectSourceResourcesSchema = object({
|
|
2642
|
+
agents: array(ProjectSourceResourceRefSchema).default([]),
|
|
2643
|
+
workflows: array(ProjectSourceResourceRefSchema).default([]),
|
|
2644
|
+
skills: array(ProjectSourceResourceRefSchema).default([])
|
|
2645
|
+
});
|
|
2646
|
+
const emptyResources = () => ({
|
|
2647
|
+
agents: [],
|
|
2648
|
+
workflows: [],
|
|
2649
|
+
skills: []
|
|
2650
|
+
});
|
|
2651
|
+
/** Response for the file-tree listing of a project's active deploy. */
|
|
2652
|
+
const ListProjectFilesResponseSchema = object({
|
|
2653
|
+
artifactId: string().nullable(),
|
|
2654
|
+
files: array(ProjectFileSummarySchema),
|
|
2655
|
+
resources: ProjectSourceResourcesSchema.default(emptyResources())
|
|
2656
|
+
});
|
|
2657
|
+
object({
|
|
2658
|
+
path: string().min(1),
|
|
2659
|
+
contents: string(),
|
|
2660
|
+
hash: sha256Hex
|
|
2661
|
+
});
|
|
2662
|
+
/** Request: ask the platform which blobs are missing and get presigned PUTs. */
|
|
2663
|
+
const PresignProjectSourceRequestSchema = object({ blobs: array(object({
|
|
2664
|
+
hash: sha256Hex,
|
|
2665
|
+
size: number$1().int().nonnegative()
|
|
2666
|
+
})) });
|
|
2667
|
+
/** Response: presigned PUTs for the subset of blobs not already stored. */
|
|
2668
|
+
const PresignProjectSourceResponseSchema = object({ uploads: array(object({
|
|
2669
|
+
hash: sha256Hex,
|
|
2670
|
+
url: string().url()
|
|
2671
|
+
})) });
|
|
2672
|
+
/** Request body the CLI sends to finalize a deploy's source manifest. */
|
|
2673
|
+
const UploadProjectSourceManifestRequestSchema = object({
|
|
2674
|
+
files: array(object({
|
|
2675
|
+
path: string().min(1),
|
|
2676
|
+
hash: sha256Hex
|
|
2677
|
+
})),
|
|
2678
|
+
resources: ProjectSourceResourcesSchema.default(emptyResources())
|
|
2679
|
+
});
|
|
2680
|
+
const UploadProjectSourceResponseSchema = object({
|
|
2681
|
+
ok: literal(true),
|
|
2682
|
+
fileCount: number$1().int().nonnegative()
|
|
2683
|
+
});
|
|
2684
|
+
object({
|
|
2685
|
+
files: array(object({
|
|
2686
|
+
id: string(),
|
|
2687
|
+
path: string().min(1),
|
|
2688
|
+
hash: sha256Hex
|
|
2689
|
+
})),
|
|
2690
|
+
resources: ProjectSourceResourcesSchema.default(emptyResources()),
|
|
2691
|
+
generatedAt: isoDateTime
|
|
2692
|
+
});
|
|
2621
2693
|
//#endregion
|
|
2622
2694
|
//#region ../../packages/sdk/dist/index.mjs
|
|
2623
2695
|
function normalizeBaseUrl$1(baseUrl) {
|
|
@@ -3122,6 +3194,69 @@ function createArtifactsResource(http) {
|
|
|
3122
3194
|
} catch (error) {
|
|
3123
3195
|
throw await toPlatformError(error);
|
|
3124
3196
|
}
|
|
3197
|
+
},
|
|
3198
|
+
/** Ask which source blobs are missing and get presigned PUTs for them. */
|
|
3199
|
+
async presignSource(projectId, artifactId, blobs) {
|
|
3200
|
+
const body = PresignProjectSourceRequestSchema.parse({ blobs });
|
|
3201
|
+
try {
|
|
3202
|
+
const data = await http.post(`/api/projects/${encodeURIComponent(projectId)}/artifacts/${encodeURIComponent(artifactId)}/source/presign`, { json: body }).json();
|
|
3203
|
+
return PresignProjectSourceResponseSchema.parse(data);
|
|
3204
|
+
} catch (error) {
|
|
3205
|
+
throw await toPlatformError(error);
|
|
3206
|
+
}
|
|
3207
|
+
},
|
|
3208
|
+
/** Finalize the deploy's source manifest (path -> hash tree + resource refs). */
|
|
3209
|
+
async uploadManifest(projectId, artifactId, manifest) {
|
|
3210
|
+
const body = UploadProjectSourceManifestRequestSchema.parse(manifest);
|
|
3211
|
+
try {
|
|
3212
|
+
const data = await http.post(`/api/projects/${encodeURIComponent(projectId)}/artifacts/${encodeURIComponent(artifactId)}/source/manifest`, { json: body }).json();
|
|
3213
|
+
return UploadProjectSourceResponseSchema.parse(data);
|
|
3214
|
+
} catch (error) {
|
|
3215
|
+
throw await toPlatformError(error);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
function createGatewayAttachmentsResource(http) {
|
|
3221
|
+
const projectPrefix = (projectId) => `/api/projects/${encodeURIComponent(projectId)}/gateways/slack/attachments`;
|
|
3222
|
+
return {
|
|
3223
|
+
async listProjectAgents(projectId) {
|
|
3224
|
+
try {
|
|
3225
|
+
const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}/agents`).json();
|
|
3226
|
+
return AgentListResponseSchema.parse(data).agents;
|
|
3227
|
+
} catch (error) {
|
|
3228
|
+
throw await toPlatformError(error);
|
|
3229
|
+
}
|
|
3230
|
+
},
|
|
3231
|
+
async upsertSlackAttachment(projectId, channelId, body) {
|
|
3232
|
+
const payload = UpsertGatewayAttachmentBodySchema.parse(body);
|
|
3233
|
+
try {
|
|
3234
|
+
const data = await http.put(`${projectPrefix(projectId)}/${encodeURIComponent(channelId)}`, { json: payload }).json();
|
|
3235
|
+
return GatewayAttachmentRecordSchema.parse(data);
|
|
3236
|
+
} catch (error) {
|
|
3237
|
+
throw await toPlatformError(error);
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
function createProjectFilesResource(http) {
|
|
3243
|
+
return {
|
|
3244
|
+
/** File tree (no contents) for a project's active deploy; empty before first deploy. */
|
|
3245
|
+
async listForProject(projectId) {
|
|
3246
|
+
try {
|
|
3247
|
+
const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}/files`).json();
|
|
3248
|
+
return ListProjectFilesResponseSchema.parse(data);
|
|
3249
|
+
} catch (error) {
|
|
3250
|
+
throw await toPlatformError(error);
|
|
3251
|
+
}
|
|
3252
|
+
},
|
|
3253
|
+
/** Lazily fetch a single file's contents by path for a specific deploy. */
|
|
3254
|
+
async getContent(projectId, artifactId, path) {
|
|
3255
|
+
try {
|
|
3256
|
+
return await http.get(`/api/projects/${encodeURIComponent(projectId)}/artifacts/${encodeURIComponent(artifactId)}/files/content`, { searchParams: { path } }).text();
|
|
3257
|
+
} catch (error) {
|
|
3258
|
+
throw await toPlatformError(error);
|
|
3259
|
+
}
|
|
3125
3260
|
}
|
|
3126
3261
|
};
|
|
3127
3262
|
}
|
|
@@ -4840,141 +4975,6 @@ const deploymentSeed = [{
|
|
|
4840
4975
|
createdAt: "2026-04-18T15:44:00Z",
|
|
4841
4976
|
active: false
|
|
4842
4977
|
}];
|
|
4843
|
-
const projectFileSeed = [
|
|
4844
|
-
{
|
|
4845
|
-
id: "file-agents-support-agent",
|
|
4846
|
-
projectId: "project_personal_repo",
|
|
4847
|
-
path: "src/agents/support-agent.ts",
|
|
4848
|
-
language: "typescript",
|
|
4849
|
-
updatedAt: "2026-04-20T15:44:00Z",
|
|
4850
|
-
contents: `import { defineAgent } from "@keystrokehq/agent";
|
|
4851
|
-
|
|
4852
|
-
export const supportAgent = defineAgent({
|
|
4853
|
-
key: "support-agent",
|
|
4854
|
-
name: "Support Agent",
|
|
4855
|
-
model: "claude-4-sonnet",
|
|
4856
|
-
instructions: "Triage support requests and draft helpful replies.",
|
|
4857
|
-
});
|
|
4858
|
-
`
|
|
4859
|
-
},
|
|
4860
|
-
{
|
|
4861
|
-
id: "file-workflows-daily-digest",
|
|
4862
|
-
projectId: "project_personal_repo",
|
|
4863
|
-
path: "src/workflows/daily-digest.ts",
|
|
4864
|
-
language: "typescript",
|
|
4865
|
-
updatedAt: "2026-04-20T15:12:00Z",
|
|
4866
|
-
contents: `import { defineWorkflow } from "@keystrokehq/workflow";
|
|
4867
|
-
|
|
4868
|
-
export const dailyDigest = defineWorkflow({
|
|
4869
|
-
key: "daily-digest",
|
|
4870
|
-
name: "Daily Digest",
|
|
4871
|
-
trigger: "schedule.daily",
|
|
4872
|
-
async run(ctx) {
|
|
4873
|
-
await ctx.step("collect-updates");
|
|
4874
|
-
await ctx.step("send-summary");
|
|
4875
|
-
},
|
|
4876
|
-
});
|
|
4877
|
-
`
|
|
4878
|
-
},
|
|
4879
|
-
{
|
|
4880
|
-
id: "file-skills-attio-sync",
|
|
4881
|
-
projectId: "project_personal_repo",
|
|
4882
|
-
path: ".agents/skills/attio-sync/SKILL.md",
|
|
4883
|
-
language: "markdown",
|
|
4884
|
-
updatedAt: "2026-04-19T18:05:00Z",
|
|
4885
|
-
contents: `# Attio Sync
|
|
4886
|
-
|
|
4887
|
-
Use this skill when a workflow needs to enrich company or person records from Attio.
|
|
4888
|
-
|
|
4889
|
-
## Inputs
|
|
4890
|
-
|
|
4891
|
-
- company domain
|
|
4892
|
-
- contact email
|
|
4893
|
-
- enrichment depth
|
|
4894
|
-
|
|
4895
|
-
## Output
|
|
4896
|
-
|
|
4897
|
-
Normalized CRM metadata ready for downstream workflow steps.
|
|
4898
|
-
`
|
|
4899
|
-
},
|
|
4900
|
-
{
|
|
4901
|
-
id: "file-skills-doc-coauthor",
|
|
4902
|
-
projectId: "project_personal_repo",
|
|
4903
|
-
path: ".agents/skills/doc-coauthor/SKILL.md",
|
|
4904
|
-
language: "markdown",
|
|
4905
|
-
updatedAt: "2026-05-30T10:24:00Z",
|
|
4906
|
-
contents: `# Doc Co-author
|
|
4907
|
-
|
|
4908
|
-
Use this skill when a workflow needs to collaboratively write, edit, and track
|
|
4909
|
-
versions of a document.
|
|
4910
|
-
|
|
4911
|
-
## Inputs
|
|
4912
|
-
|
|
4913
|
-
- document title
|
|
4914
|
-
- source outline or draft
|
|
4915
|
-
- target audience
|
|
4916
|
-
|
|
4917
|
-
## Output
|
|
4918
|
-
|
|
4919
|
-
A revised document with tracked changes and a short summary of edits.
|
|
4920
|
-
`
|
|
4921
|
-
},
|
|
4922
|
-
{
|
|
4923
|
-
id: "file-skills-doc-coauthor-reference",
|
|
4924
|
-
projectId: "project_personal_repo",
|
|
4925
|
-
path: ".agents/skills/doc-coauthor/reference.md",
|
|
4926
|
-
language: "markdown",
|
|
4927
|
-
updatedAt: "2026-05-30T10:24:00Z",
|
|
4928
|
-
contents: `# Reference
|
|
4929
|
-
|
|
4930
|
-
Style and structure guidance the co-author follows when editing docs.
|
|
4931
|
-
|
|
4932
|
-
## Voice
|
|
4933
|
-
|
|
4934
|
-
- Prefer active voice and short sentences.
|
|
4935
|
-
- Keep headings consistent with the existing document.
|
|
4936
|
-
|
|
4937
|
-
## Versioning
|
|
4938
|
-
|
|
4939
|
-
- Summarize each revision pass.
|
|
4940
|
-
- Never drop the reader's original intent.
|
|
4941
|
-
`
|
|
4942
|
-
},
|
|
4943
|
-
{
|
|
4944
|
-
id: "file-skills-doc-coauthor-template",
|
|
4945
|
-
projectId: "project_personal_repo",
|
|
4946
|
-
path: ".agents/skills/doc-coauthor/templates/outline.md",
|
|
4947
|
-
language: "markdown",
|
|
4948
|
-
updatedAt: "2026-05-30T10:24:00Z",
|
|
4949
|
-
contents: `# {{title}}
|
|
4950
|
-
|
|
4951
|
-
## Summary
|
|
4952
|
-
|
|
4953
|
-
## Background
|
|
4954
|
-
|
|
4955
|
-
## Details
|
|
4956
|
-
|
|
4957
|
-
## Next steps
|
|
4958
|
-
`
|
|
4959
|
-
},
|
|
4960
|
-
{
|
|
4961
|
-
id: "file-actions-send-slack",
|
|
4962
|
-
projectId: "project_personal_repo",
|
|
4963
|
-
path: "src/actions/send-slack-message.ts",
|
|
4964
|
-
language: "typescript",
|
|
4965
|
-
updatedAt: "2026-04-18T12:30:00Z",
|
|
4966
|
-
contents: `import { defineAction } from "@keystrokehq/action";
|
|
4967
|
-
|
|
4968
|
-
export const sendSlackMessage = defineAction({
|
|
4969
|
-
key: "send-slack-message",
|
|
4970
|
-
name: "Send Slack message",
|
|
4971
|
-
async run({ channel, text }) {
|
|
4972
|
-
return { channel, text, sent: true };
|
|
4973
|
-
},
|
|
4974
|
-
});
|
|
4975
|
-
`
|
|
4976
|
-
}
|
|
4977
|
-
];
|
|
4978
4978
|
/** Review personas for mocked project members and API key authors (not org membership). */
|
|
4979
4979
|
const mockPersonaSeed = [
|
|
4980
4980
|
{
|
|
@@ -5293,16 +5293,6 @@ function createProjectMetricsResource() {
|
|
|
5293
5293
|
plannedEndpoint: "GET /api/projects/metrics"
|
|
5294
5294
|
}, async () => ({})) };
|
|
5295
5295
|
}
|
|
5296
|
-
function createProjectFilesResource() {
|
|
5297
|
-
return { listForProject: mock({
|
|
5298
|
-
domain: "projectFiles",
|
|
5299
|
-
method: "listForProject",
|
|
5300
|
-
plannedEndpoint: "GET /api/projects/:id/files"
|
|
5301
|
-
}, async (projectId) => projectFileSeed.map((file) => ({
|
|
5302
|
-
...file,
|
|
5303
|
-
projectId
|
|
5304
|
-
}))) };
|
|
5305
|
-
}
|
|
5306
5296
|
const projectMembersStore = /* @__PURE__ */ new Map();
|
|
5307
5297
|
function getProjectMembers(projectId) {
|
|
5308
5298
|
let members = projectMembersStore.get(projectId);
|
|
@@ -6916,8 +6906,9 @@ function createPlatformClient(options) {
|
|
|
6916
6906
|
credentials: createCredentialsResource(),
|
|
6917
6907
|
history: createHistoryResource(),
|
|
6918
6908
|
deployments: createDeploymentsResource(),
|
|
6909
|
+
gatewayAttachments: createGatewayAttachmentsResource(http),
|
|
6919
6910
|
projectMetrics: createProjectMetricsResource(),
|
|
6920
|
-
projectFiles: createProjectFilesResource(),
|
|
6911
|
+
projectFiles: createProjectFilesResource(http),
|
|
6921
6912
|
channels: createChannelsResource(),
|
|
6922
6913
|
members: createMembersResource(http),
|
|
6923
6914
|
invitations: createInvitationsResource(http),
|
|
@@ -9254,7 +9245,7 @@ function registerBuildCommand(program) {
|
|
|
9254
9245
|
program.command("build").description("Build the keystroke project for production").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
|
|
9255
9246
|
try {
|
|
9256
9247
|
const root = resolveProjectRoot(options.dir);
|
|
9257
|
-
const { buildApp } = await import("./dist-
|
|
9248
|
+
const { buildApp } = await import("./dist-IKyaKHgU.mjs");
|
|
9258
9249
|
await buildApp({ root });
|
|
9259
9250
|
process.stdout.write(`Built ${root}\n`);
|
|
9260
9251
|
} catch (error) {
|
|
@@ -9297,9 +9288,140 @@ function packProjectArtifact(projectRoot) {
|
|
|
9297
9288
|
}
|
|
9298
9289
|
}
|
|
9299
9290
|
//#endregion
|
|
9291
|
+
//#region src/project/collect-source.ts
|
|
9292
|
+
const IGNORED_DIRS = new Set([
|
|
9293
|
+
"node_modules",
|
|
9294
|
+
"dist",
|
|
9295
|
+
".git",
|
|
9296
|
+
".turbo",
|
|
9297
|
+
"build",
|
|
9298
|
+
"coverage",
|
|
9299
|
+
".keystroke",
|
|
9300
|
+
".cache",
|
|
9301
|
+
"tmp"
|
|
9302
|
+
]);
|
|
9303
|
+
const MAX_FILE_BYTES = 256 * 1024;
|
|
9304
|
+
const MAX_TOTAL_BYTES = 8 * 1024 * 1024;
|
|
9305
|
+
const MAX_FILES = 2e3;
|
|
9306
|
+
function toPosix(path) {
|
|
9307
|
+
return path.split(sep).join("/");
|
|
9308
|
+
}
|
|
9309
|
+
function isProbablyBinary(buffer) {
|
|
9310
|
+
const sampleLength = Math.min(buffer.length, 8e3);
|
|
9311
|
+
for (let index = 0; index < sampleLength; index += 1) if (buffer[index] === 0) return true;
|
|
9312
|
+
return false;
|
|
9313
|
+
}
|
|
9314
|
+
function walkFiles(root, dir, out, totals) {
|
|
9315
|
+
for (const name of readdirSync(dir).sort()) {
|
|
9316
|
+
if (out.length >= MAX_FILES || totals.bytes >= MAX_TOTAL_BYTES) return;
|
|
9317
|
+
const absolute = join(dir, name);
|
|
9318
|
+
const stats = statSync(absolute);
|
|
9319
|
+
if (stats.isDirectory()) {
|
|
9320
|
+
if (IGNORED_DIRS.has(name)) continue;
|
|
9321
|
+
walkFiles(root, absolute, out, totals);
|
|
9322
|
+
continue;
|
|
9323
|
+
}
|
|
9324
|
+
if (!stats.isFile() || stats.size > MAX_FILE_BYTES) continue;
|
|
9325
|
+
const buffer = readFileSync(absolute);
|
|
9326
|
+
if (isProbablyBinary(buffer)) continue;
|
|
9327
|
+
totals.bytes += buffer.byteLength;
|
|
9328
|
+
out.push({
|
|
9329
|
+
path: toPosix(relative(root, absolute)),
|
|
9330
|
+
contents: buffer.toString("utf8"),
|
|
9331
|
+
hash: createHash("sha256").update(buffer).digest("hex")
|
|
9332
|
+
});
|
|
9333
|
+
}
|
|
9334
|
+
}
|
|
9335
|
+
function posixPrefix(root, srcRoot, subdir) {
|
|
9336
|
+
return `${toPosix(relative(root, join(srcRoot, subdir)))}/`;
|
|
9337
|
+
}
|
|
9338
|
+
function collectModuleRefs(srcRoot, root, files, subdir, nestedEntry) {
|
|
9339
|
+
const dir = join(srcRoot, subdir);
|
|
9340
|
+
const prefix = posixPrefix(root, srcRoot, subdir);
|
|
9341
|
+
const refs = [];
|
|
9342
|
+
for (const file of files) {
|
|
9343
|
+
if (!file.path.startsWith(prefix) || !/\.(ts|mts)$/.test(file.path)) continue;
|
|
9344
|
+
const key = entryIdFromFile(dir, join(root, file.path), { nestedEntry });
|
|
9345
|
+
if (key) refs.push({
|
|
9346
|
+
key,
|
|
9347
|
+
path: file.path
|
|
9348
|
+
});
|
|
9349
|
+
}
|
|
9350
|
+
return refs;
|
|
9351
|
+
}
|
|
9352
|
+
function collectSkillRefs(srcRoot, root, files) {
|
|
9353
|
+
const dir = join(srcRoot, "skills");
|
|
9354
|
+
const prefix = posixPrefix(root, srcRoot, "skills");
|
|
9355
|
+
const refs = [];
|
|
9356
|
+
for (const file of files) {
|
|
9357
|
+
if (!file.path.startsWith(prefix) || !file.path.endsWith("SKILL.md")) continue;
|
|
9358
|
+
const key = toPosix(relative(dir, join(root, file.path)).replace(/\/?SKILL\.md$/, ""));
|
|
9359
|
+
if (key) refs.push({
|
|
9360
|
+
key,
|
|
9361
|
+
path: file.path
|
|
9362
|
+
});
|
|
9363
|
+
}
|
|
9364
|
+
return refs;
|
|
9365
|
+
}
|
|
9366
|
+
/**
|
|
9367
|
+
* Collect the project's actual source code into a deploy snapshot: every text
|
|
9368
|
+
* file under the project root (minus build output / deps) plus a map of each
|
|
9369
|
+
* agent / workflow / skill key to its source path. The map mirrors the same
|
|
9370
|
+
* path-derived ids the build step uses.
|
|
9371
|
+
*/
|
|
9372
|
+
function collectProjectSource(root) {
|
|
9373
|
+
const files = [];
|
|
9374
|
+
walkFiles(root, root, files, { bytes: 0 });
|
|
9375
|
+
const srcRoot = join(root, "src");
|
|
9376
|
+
return {
|
|
9377
|
+
files,
|
|
9378
|
+
resources: {
|
|
9379
|
+
agents: collectModuleRefs(srcRoot, root, files, "agents", "agent"),
|
|
9380
|
+
workflows: collectModuleRefs(srcRoot, root, files, "workflows", "workflow"),
|
|
9381
|
+
skills: collectSkillRefs(srcRoot, root, files)
|
|
9382
|
+
}
|
|
9383
|
+
};
|
|
9384
|
+
}
|
|
9385
|
+
//#endregion
|
|
9300
9386
|
//#region src/commands/deploy.ts
|
|
9301
9387
|
const POLL_INTERVAL_MS = 2e3;
|
|
9302
9388
|
const DEPLOY_TIMEOUT_MS = 12e4;
|
|
9389
|
+
const SOURCE_BLOB_CONTENT_TYPE = "text/plain; charset=utf-8";
|
|
9390
|
+
/**
|
|
9391
|
+
* Upload the project's source so the dashboard can render a read-only file
|
|
9392
|
+
* view. Content-addressed: only blobs the platform reports missing are PUT, so
|
|
9393
|
+
* unchanged files are never re-uploaded. The manifest (path -> hash) is sent
|
|
9394
|
+
* last and marks the snapshot complete.
|
|
9395
|
+
*/
|
|
9396
|
+
async function uploadProjectSourceSnapshot(client, projectId, artifactId, source) {
|
|
9397
|
+
const contentsByHash = /* @__PURE__ */ new Map();
|
|
9398
|
+
const blobsByHash = /* @__PURE__ */ new Map();
|
|
9399
|
+
for (const file of source.files) {
|
|
9400
|
+
contentsByHash.set(file.hash, file.contents);
|
|
9401
|
+
if (!blobsByHash.has(file.hash)) blobsByHash.set(file.hash, {
|
|
9402
|
+
hash: file.hash,
|
|
9403
|
+
size: Buffer.byteLength(file.contents)
|
|
9404
|
+
});
|
|
9405
|
+
}
|
|
9406
|
+
const { uploads } = await client.artifacts.presignSource(projectId, artifactId, [...blobsByHash.values()]);
|
|
9407
|
+
await Promise.all(uploads.map(async (upload) => {
|
|
9408
|
+
const contents = contentsByHash.get(upload.hash);
|
|
9409
|
+
if (contents === void 0) return;
|
|
9410
|
+
const response = await fetch(upload.url, {
|
|
9411
|
+
method: "PUT",
|
|
9412
|
+
body: contents,
|
|
9413
|
+
headers: { "Content-Type": SOURCE_BLOB_CONTENT_TYPE }
|
|
9414
|
+
});
|
|
9415
|
+
if (!response.ok) throw new Error(`Source blob upload failed (${response.status})`);
|
|
9416
|
+
}));
|
|
9417
|
+
await client.artifacts.uploadManifest(projectId, artifactId, {
|
|
9418
|
+
files: source.files.map((file) => ({
|
|
9419
|
+
path: file.path,
|
|
9420
|
+
hash: file.hash
|
|
9421
|
+
})),
|
|
9422
|
+
resources: source.resources
|
|
9423
|
+
});
|
|
9424
|
+
}
|
|
9303
9425
|
async function sleep(ms) {
|
|
9304
9426
|
await new Promise((resolve) => {
|
|
9305
9427
|
setTimeout(resolve, ms);
|
|
@@ -9310,7 +9432,7 @@ async function runDeploy(options) {
|
|
|
9310
9432
|
const config = createCliConfig();
|
|
9311
9433
|
const client = createCliPlatformClient(config);
|
|
9312
9434
|
if (!options.skipBuild) {
|
|
9313
|
-
const { buildApp } = await import("./dist-
|
|
9435
|
+
const { buildApp } = await import("./dist-IKyaKHgU.mjs");
|
|
9314
9436
|
await buildApp({ root });
|
|
9315
9437
|
}
|
|
9316
9438
|
const archive = packProjectArtifact(root);
|
|
@@ -9318,12 +9440,19 @@ async function runDeploy(options) {
|
|
|
9318
9440
|
if (!active) active = await selectActiveOrganization(config);
|
|
9319
9441
|
client.setActiveOrganizationId(active.organization.id);
|
|
9320
9442
|
const project = await client.projects.get(options.projectId);
|
|
9443
|
+
const source = collectProjectSource(root);
|
|
9321
9444
|
const created = await client.artifacts.create(project.id);
|
|
9322
|
-
const
|
|
9445
|
+
const uploadPromise = fetch(created.uploadUrl, {
|
|
9323
9446
|
method: "PUT",
|
|
9324
9447
|
body: archive,
|
|
9325
9448
|
headers: { "Content-Type": "application/gzip" }
|
|
9326
9449
|
});
|
|
9450
|
+
const sourcePromise = uploadProjectSourceSnapshot(client, project.id, created.artifact.id, source).catch((error) => {
|
|
9451
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
9452
|
+
process.stderr.write(`Warning: failed to upload project source files: ${message}\n`);
|
|
9453
|
+
});
|
|
9454
|
+
const upload = await uploadPromise;
|
|
9455
|
+
await sourcePromise;
|
|
9327
9456
|
if (!upload.ok) throw new Error(`Artifact upload failed (${upload.status})`);
|
|
9328
9457
|
const completed = await client.artifacts.complete(project.id, created.artifact.id);
|
|
9329
9458
|
process.stdout.write(`Deploy ${completed.artifact.id} uploaded\n`);
|
|
@@ -9401,7 +9530,7 @@ function runtimeChildEnv(parentEnv, overrides) {
|
|
|
9401
9530
|
//#region src/project/bootstrap-run.ts
|
|
9402
9531
|
/** Node args + env for `@keystrokehq/build` bootstrap (shared by start + dev). */
|
|
9403
9532
|
async function resolveBootstrapRun(options) {
|
|
9404
|
-
const { resolveRuntimeBuildArtifact } = await import("./dist-
|
|
9533
|
+
const { resolveRuntimeBuildArtifact } = await import("./dist-IKyaKHgU.mjs");
|
|
9405
9534
|
const loader = pathToFileURL(resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/runtime-loader.mjs")).href;
|
|
9406
9535
|
const bootstrap = resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/bootstrap.mjs");
|
|
9407
9536
|
const args = [`--import=${loader}`];
|
|
@@ -9551,7 +9680,7 @@ async function runDev(options) {
|
|
|
9551
9680
|
process.on("SIGINT", shutdown);
|
|
9552
9681
|
process.on("SIGTERM", shutdown);
|
|
9553
9682
|
try {
|
|
9554
|
-
const { watchApp } = await import("./dist-
|
|
9683
|
+
const { watchApp } = await import("./dist-IKyaKHgU.mjs");
|
|
9555
9684
|
await watchApp({
|
|
9556
9685
|
root,
|
|
9557
9686
|
clean: false,
|
|
@@ -9946,7 +10075,7 @@ async function runStart(options) {
|
|
|
9946
10075
|
const apiPort = Number(new URL(serverUrl).port || 80);
|
|
9947
10076
|
const runtimeNodeModules = resolveCliRuntimeNodeModules(resolveCliRoot(import.meta.url));
|
|
9948
10077
|
ensureNativeDeps(runtimeNodeModules);
|
|
9949
|
-
const { buildApp } = await import("./dist-
|
|
10078
|
+
const { buildApp } = await import("./dist-IKyaKHgU.mjs");
|
|
9950
10079
|
await buildApp({
|
|
9951
10080
|
root,
|
|
9952
10081
|
clean: false
|