@keystrokehq/hosting 0.1.8 → 0.1.10

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.cjs CHANGED
@@ -34,6 +34,10 @@ const PROJECT_SERVER_HEALTH = {
34
34
  path: "/health",
35
35
  port: PROJECT_SERVER_PORT
36
36
  };
37
+ const PROJECT_SERVER_DEPLOY_STATUS = {
38
+ path: "/deploy-status",
39
+ port: PROJECT_SERVER_PORT
40
+ };
37
41
  //#endregion
38
42
  //#region ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
39
43
  var _a$1;
@@ -641,6 +645,7 @@ const string$1 = (params) => {
641
645
  const integer = /^-?\d+$/;
642
646
  const number$2 = /^-?\d+(?:\.\d+)?$/;
643
647
  const boolean$1 = /^(?:true|false)$/i;
648
+ const _null$2 = /^null$/i;
644
649
  const lowercase = /^[^A-Z]*$/;
645
650
  const uppercase = /^[^a-z]*$/;
646
651
  //#endregion
@@ -1453,6 +1458,22 @@ const $ZodBoolean = /* @__PURE__ */ $constructor("$ZodBoolean", (inst, def) => {
1453
1458
  return payload;
1454
1459
  };
1455
1460
  });
1461
+ const $ZodNull = /* @__PURE__ */ $constructor("$ZodNull", (inst, def) => {
1462
+ $ZodType.init(inst, def);
1463
+ inst._zod.pattern = _null$2;
1464
+ inst._zod.values = new Set([null]);
1465
+ inst._zod.parse = (payload, _ctx) => {
1466
+ const input = payload.value;
1467
+ if (input === null) return payload;
1468
+ payload.issues.push({
1469
+ expected: "null",
1470
+ code: "invalid_type",
1471
+ input,
1472
+ inst
1473
+ });
1474
+ return payload;
1475
+ };
1476
+ });
1456
1477
  const $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
1457
1478
  $ZodType.init(inst, def);
1458
1479
  inst._zod.parse = (payload) => payload;
@@ -2699,6 +2720,13 @@ function _boolean(Class, params) {
2699
2720
  });
2700
2721
  }
2701
2722
  /* @__NO_SIDE_EFFECTS__ */
2723
+ function _null$1(Class, params) {
2724
+ return new Class({
2725
+ type: "null",
2726
+ ...normalizeParams(params)
2727
+ });
2728
+ }
2729
+ /* @__NO_SIDE_EFFECTS__ */
2702
2730
  function _unknown(Class) {
2703
2731
  return new Class({ type: "unknown" });
2704
2732
  }
@@ -3247,6 +3275,13 @@ const numberProcessor = (schema, ctx, _json, _params) => {
3247
3275
  const booleanProcessor = (_schema, _ctx, json, _params) => {
3248
3276
  json.type = "boolean";
3249
3277
  };
3278
+ const nullProcessor = (_schema, ctx, json, _params) => {
3279
+ if (ctx.target === "openapi-3.0") {
3280
+ json.type = "string";
3281
+ json.nullable = true;
3282
+ json.enum = [null];
3283
+ } else json.type = "null";
3284
+ };
3250
3285
  const neverProcessor = (_schema, _ctx, json, _params) => {
3251
3286
  json.not = {};
3252
3287
  };
@@ -3946,6 +3981,14 @@ const ZodBoolean = /* @__PURE__ */ $constructor("ZodBoolean", (inst, def) => {
3946
3981
  function boolean(params) {
3947
3982
  return /* @__PURE__ */ _boolean(ZodBoolean, params);
3948
3983
  }
3984
+ const ZodNull = /* @__PURE__ */ $constructor("ZodNull", (inst, def) => {
3985
+ $ZodNull.init(inst, def);
3986
+ ZodType.init(inst, def);
3987
+ inst._zod.processJSONSchema = (ctx, json, params) => nullProcessor(inst, ctx, json, params);
3988
+ });
3989
+ function _null(params) {
3990
+ return /* @__PURE__ */ _null$1(ZodNull, params);
3991
+ }
3949
3992
  const ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
3950
3993
  $ZodUnknown.init(inst, def);
3951
3994
  ZodType.init(inst, def);
@@ -5780,6 +5823,8 @@ const nullableOptionalTimestamp = string().nullable().optional();
5780
5823
  object({
5781
5824
  id: string().min(1),
5782
5825
  appId: string().min(1),
5826
+ /** Stable per-scope identifier; pass to `--credential` to pin a specific instance. */
5827
+ slug: string().min(1),
5783
5828
  label: string().min(1),
5784
5829
  scope: AppCredentialScopeSchema,
5785
5830
  lastRefreshedAt: string().nullable(),
@@ -5936,7 +5981,11 @@ object({
5936
5981
  /** When set, platform MCP resolve checks credential assignments before scope defaults. */
5937
5982
  assignmentTarget: McpCredentialAssignmentTargetSchema.optional(),
5938
5983
  /** Tool/step consumer id for assignment lookup (action slug or workflow step correlation id). */
5939
- consumerId: string().trim().min(1).optional()
5984
+ consumerId: string().trim().min(1).optional(),
5985
+ /** Explicit credential instance slug; overrides default resolution to pick that instance. */
5986
+ credentialSlug: string().trim().min(1).optional(),
5987
+ /** Project slug (CLI --project); the platform resolves it to a project and binds that scope. */
5988
+ projectSlug: string().trim().min(1).optional()
5940
5989
  });
5941
5990
  object({
5942
5991
  error: _enum([
@@ -6937,6 +6986,14 @@ object({
6937
6986
  uploadUrl: url(),
6938
6987
  storageKey: string().min(1)
6939
6988
  });
6989
+ function isValidIanaTimezone(value) {
6990
+ try {
6991
+ Intl.DateTimeFormat(void 0, { timeZone: value });
6992
+ return true;
6993
+ } catch {
6994
+ return false;
6995
+ }
6996
+ }
6940
6997
  const UserInterfaceThemeSchema = _enum([
6941
6998
  "system",
6942
6999
  "light",
@@ -6956,22 +7013,25 @@ const UserWorkspaceFavoriteSchema = object({
6956
7013
  ]),
6957
7014
  resourceId: string().min(1)
6958
7015
  });
7016
+ const UserSidebarPreferencesSchema = object({
7017
+ navIconVisibility: UserSidebarNavIconVisibilitySchema,
7018
+ footerChatRowVisibility: UserSidebarFooterChatRowVisibilitySchema,
7019
+ hiddenNavItemIds: array(string()),
7020
+ topNavItemOrderIds: array(string()),
7021
+ workspaceNavItemOrderIds: array(string())
7022
+ });
7023
+ const UserTimezoneSchema = string().refine(isValidIanaTimezone, { message: "Invalid IANA timezone" });
6959
7024
  object({
6960
7025
  interfaceTheme: UserInterfaceThemeSchema,
6961
7026
  surfaceContrast: UserSurfaceContrastSchema,
6962
7027
  layoutMode: UserWorkspaceLayoutModeSchema,
6963
7028
  fontSize: UserWorkspaceFontSizeSchema,
6964
- sidebar: object({
6965
- navIconVisibility: UserSidebarNavIconVisibilitySchema,
6966
- footerChatRowVisibility: UserSidebarFooterChatRowVisibilitySchema,
6967
- hiddenNavItemIds: array(string()),
6968
- topNavItemOrderIds: array(string()),
6969
- workspaceNavItemOrderIds: array(string())
6970
- }),
7029
+ sidebar: UserSidebarPreferencesSchema,
6971
7030
  favorites: array(UserWorkspaceFavoriteSchema),
6972
7031
  projectOrderIds: array(string()),
6973
7032
  projectDotColors: record(string(), string()),
6974
- onboardingTabVisible: boolean()
7033
+ onboardingTabVisible: boolean(),
7034
+ timezone: union([UserTimezoneSchema, _null()])
6975
7035
  });
6976
7036
  const UserSidebarPreferencesPatchSchema = object({
6977
7037
  navIconVisibility: UserSidebarNavIconVisibilitySchema.optional(),
@@ -6989,8 +7049,9 @@ object({
6989
7049
  favorites: array(UserWorkspaceFavoriteSchema).optional(),
6990
7050
  projectOrderIds: array(string()).optional(),
6991
7051
  projectDotColors: record(string(), string()).optional(),
6992
- onboardingTabVisible: boolean().optional()
6993
- }).refine((value) => value.interfaceTheme !== void 0 || value.surfaceContrast !== void 0 || value.layoutMode !== void 0 || value.fontSize !== void 0 || value.sidebar !== void 0 || value.favorites !== void 0 || value.projectOrderIds !== void 0 || value.projectDotColors !== void 0 || value.onboardingTabVisible !== void 0, { message: "At least one preference field is required" });
7052
+ onboardingTabVisible: boolean().optional(),
7053
+ timezone: union([UserTimezoneSchema, _null()]).optional()
7054
+ }).refine((value) => value.interfaceTheme !== void 0 || value.surfaceContrast !== void 0 || value.layoutMode !== void 0 || value.fontSize !== void 0 || value.sidebar !== void 0 || value.favorites !== void 0 || value.projectOrderIds !== void 0 || value.projectDotColors !== void 0 || value.onboardingTabVisible !== void 0 || value.timezone !== void 0, { message: "At least one preference field is required" });
6994
7055
  const OrganizationLogoVariantSchema = _enum(["light", "dark"]);
6995
7056
  object({
6996
7057
  customLogoEnabled: boolean(),
@@ -7619,55 +7680,63 @@ function sleep(ms) {
7619
7680
  const MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? "keystroke";
7620
7681
  function createDockerRuntime(options = {}) {
7621
7682
  const docker = options.docker ?? new dockerode.default();
7622
- return { async start(input) {
7623
- if (input.projects.length === 0) throw new Error("Docker org runtime requires at least one project deploy");
7624
- let launch = resolveRuntimeLaunch(options, input.projects.map((project) => ({
7625
- projectId: project.projectId,
7626
- artifactStorageKey: project.artifactStorageKey
7627
- })), input.database, input.env);
7628
- if (options.workspacesMount) {
7629
- const { hostPath, containerPath } = options.workspacesMount;
7630
- await (0, node_fs_promises.mkdir)(hostPath, { recursive: true });
7631
- launch = {
7632
- ...launch,
7633
- env: {
7634
- ...launch.env,
7635
- KEYSTROKE_WORKSPACES_ROOT: containerPath
7636
- }
7683
+ return {
7684
+ async start(input) {
7685
+ if (input.projects.length === 0) throw new Error("Docker org runtime requires at least one project deploy");
7686
+ let launch = resolveRuntimeLaunch(options, input.projects.map((project) => ({
7687
+ projectId: project.projectId,
7688
+ artifactStorageKey: project.artifactStorageKey
7689
+ })), input.database, input.env);
7690
+ if (options.workspacesMount) {
7691
+ const { hostPath, containerPath } = options.workspacesMount;
7692
+ await (0, node_fs_promises.mkdir)(hostPath, { recursive: true });
7693
+ launch = {
7694
+ ...launch,
7695
+ env: {
7696
+ ...launch.env,
7697
+ KEYSTROKE_WORKSPACES_ROOT: containerPath
7698
+ }
7699
+ };
7700
+ }
7701
+ await ensureImage(docker, launch.image);
7702
+ await removeExistingContainer(docker, input.organizationId);
7703
+ const binds = [...options.workspacesMount ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`] : [], ...options.extraBinds ?? []];
7704
+ const container = await docker.createContainer({
7705
+ Image: launch.image,
7706
+ name: containerName(input.organizationId),
7707
+ Labels: {
7708
+ "keystroke.organization.id": input.organizationId,
7709
+ "keystroke.project.ids": input.projects.map((project) => project.projectId).join(",")
7710
+ },
7711
+ ExposedPorts: { [`${launch.port}/tcp`]: {} },
7712
+ HostConfig: {
7713
+ ExtraHosts: ["host.docker.internal:host-gateway"],
7714
+ PortBindings: { [`${launch.port}/tcp`]: [{ HostPort: "0" }] },
7715
+ ...binds.length > 0 ? { Binds: binds } : {}
7716
+ },
7717
+ NetworkingConfig: { EndpointsConfig: { [MINIO_DOCKER_NETWORK]: {} } },
7718
+ Env: formatDockerEnv(launch.env)
7719
+ });
7720
+ await container.start();
7721
+ const inspect = await container.inspect();
7722
+ const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];
7723
+ if (!binding?.HostPort) throw new Error("Failed to resolve org container host port");
7724
+ const baseUrl = `http://127.0.0.1:${binding.HostPort}`;
7725
+ await waitForHealth(baseUrl, {
7726
+ fetchImpl: options.fetchImpl,
7727
+ timeoutMs: options.healthTimeoutMs
7728
+ });
7729
+ return {
7730
+ runtimeId: inspect.Id,
7731
+ baseUrl
7637
7732
  };
7733
+ },
7734
+ async destroy(input) {
7735
+ try {
7736
+ await docker.getContainer(input.runtimeId).remove({ force: true });
7737
+ } catch {}
7638
7738
  }
7639
- await ensureImage(docker, launch.image);
7640
- await removeExistingContainer(docker, input.organizationId);
7641
- const container = await docker.createContainer({
7642
- Image: launch.image,
7643
- name: containerName(input.organizationId),
7644
- Labels: {
7645
- "keystroke.organization.id": input.organizationId,
7646
- "keystroke.project.ids": input.projects.map((project) => project.projectId).join(",")
7647
- },
7648
- ExposedPorts: { [`${launch.port}/tcp`]: {} },
7649
- HostConfig: {
7650
- ExtraHosts: ["host.docker.internal:host-gateway"],
7651
- PortBindings: { [`${launch.port}/tcp`]: [{ HostPort: "0" }] },
7652
- ...options.workspacesMount ? { Binds: [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`] } : {}
7653
- },
7654
- NetworkingConfig: { EndpointsConfig: { [MINIO_DOCKER_NETWORK]: {} } },
7655
- Env: formatDockerEnv(launch.env)
7656
- });
7657
- await container.start();
7658
- const inspect = await container.inspect();
7659
- const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];
7660
- if (!binding?.HostPort) throw new Error("Failed to resolve org container host port");
7661
- const baseUrl = `http://127.0.0.1:${binding.HostPort}`;
7662
- await waitForHealth(baseUrl, {
7663
- fetchImpl: options.fetchImpl,
7664
- timeoutMs: options.healthTimeoutMs
7665
- });
7666
- return {
7667
- runtimeId: inspect.Id,
7668
- baseUrl
7669
- };
7670
- } };
7739
+ };
7671
7740
  }
7672
7741
  function containerName(organizationId) {
7673
7742
  return `keystroke-org-${organizationId}`;
@@ -7755,8 +7824,29 @@ async function pingProjectTarget(target, options = {}) {
7755
7824
  });
7756
7825
  }
7757
7826
  //#endregion
7827
+ //#region src/deploy-status.ts
7828
+ /**
7829
+ * Ask a running org machine which projects bootstrapped and which failed.
7830
+ * Best-effort: returns undefined on any error so callers can fall back to
7831
+ * treating every project as healthy (preserving pre-existing behavior).
7832
+ */
7833
+ async function fetchDeployStatus(baseUrl, options = {}) {
7834
+ const timeoutMs = options.timeoutMs ?? 15e3;
7835
+ try {
7836
+ const response = await (options.fetchImpl ?? fetch)(new URL(PROJECT_SERVER_DEPLOY_STATUS.path, baseUrl), { signal: AbortSignal.timeout(timeoutMs) });
7837
+ if (!response.ok) return;
7838
+ const data = await response.json();
7839
+ if (!data || !Array.isArray(data.projects)) return;
7840
+ return data;
7841
+ } catch (error) {
7842
+ console.warn(`[deploy-status] failed to fetch from ${baseUrl}:`, error);
7843
+ return;
7844
+ }
7845
+ }
7846
+ //#endregion
7758
7847
  exports.DEV_PLATFORM_WORKER_TOKEN = DEV_PLATFORM_WORKER_TOKEN;
7759
7848
  exports.PROJECT_DATABASE_ENV_KEYS = PROJECT_DATABASE_ENV_KEYS;
7849
+ exports.PROJECT_SERVER_DEPLOY_STATUS = PROJECT_SERVER_DEPLOY_STATUS;
7760
7850
  exports.PROJECT_SERVER_FRAMEWORK_NODE_MODULES = PROJECT_SERVER_FRAMEWORK_NODE_MODULES;
7761
7851
  exports.PROJECT_SERVER_FRAMEWORK_ROOT = PROJECT_SERVER_FRAMEWORK_ROOT;
7762
7852
  exports.PROJECT_SERVER_HEALTH = PROJECT_SERVER_HEALTH;
@@ -7770,6 +7860,7 @@ exports.canPingProjectTarget = canPingProjectTarget;
7770
7860
  exports.defaultHostingPlugin = defaultHostingPlugin;
7771
7861
  exports.dockerPlugin = dockerPlugin;
7772
7862
  exports.encodeOrgArtifacts = encodeOrgArtifacts;
7863
+ exports.fetchDeployStatus = fetchDeployStatus;
7773
7864
  exports.formatDockerEnv = formatDockerEnv;
7774
7865
  exports.parseOrgArtifactsFromEnv = parseOrgArtifactsFromEnv;
7775
7866
  exports.pingProject = pingProject;