@roamcode.ai/server 1.2.0 → 1.4.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/start.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { FastifyInstance } from 'fastify';
2
+ import { z } from 'zod';
2
3
 
3
4
  /**
4
5
  * Wiring for the mcp-send server (Claude → user attachments). When present, the spawn layer writes a
@@ -184,7 +185,7 @@ type AuthCheckResult = {
184
185
  * WebSocket upgrade.
185
186
  *
186
187
  * Proxy lockout caveat: the lockout is keyed by `clientKey` (`request.ip`).
187
- * Behind a reverse proxy (Caddy / Cloudflare) `request.ip` is the proxy's IP
188
+ * Behind a reverse proxy, `request.ip` is the proxy's IP
188
189
  * for EVERY client, collapsing the per-client lockout to one shared key. To
189
190
  * stop that from self-DoS-ing the sole legitimate user, {@link check} validates
190
191
  * the token FIRST: a CORRECT token is ALWAYS accepted, even while its key is
@@ -369,6 +370,134 @@ interface PairingTicket {
369
370
  scopes: DeviceScope[];
370
371
  }
371
372
 
373
+ declare const CloudAutomationWebhookRegistrationSchema: z.ZodObject<{
374
+ hookId: z.ZodString;
375
+ automationId: z.ZodString;
376
+ triggerId: z.ZodString;
377
+ secretHash: z.ZodString;
378
+ enabled: z.ZodBoolean;
379
+ }, z.core.$strict>;
380
+ declare const CloudAutomationInvocationSchema: z.ZodObject<{
381
+ id: z.ZodUUID;
382
+ automationId: z.ZodString;
383
+ triggerId: z.ZodString;
384
+ hookId: z.ZodString;
385
+ createdAt: z.ZodNumber;
386
+ }, z.core.$strict>;
387
+ type CloudAutomationWebhookRegistration = z.infer<typeof CloudAutomationWebhookRegistrationSchema>;
388
+ type CloudAutomationInvocation = z.infer<typeof CloudAutomationInvocationSchema>;
389
+
390
+ type TeamStoreMode = "sqlite" | "memory-fallback";
391
+ type TeamMemberKind = "person" | "service";
392
+ type TeamMemberStatus = "active" | "suspended" | "removed";
393
+ type TeamRole = "viewer" | "operator" | "node-admin" | "workspace-manager" | "extension-manager" | "policy-admin" | "organization-admin";
394
+ type TeamScopeType = "team" | "host" | "workspace";
395
+ type TeamPrincipalType = "device" | "host" | "local" | "relay";
396
+ type TeamPermission = "team:read" | "sessions:read" | "sessions:operate" | "attention:read" | "attention:manage" | "presence:read" | "presence:write" | "workspaces:manage" | "extensions:manage" | "policy:manage" | "members:manage" | "node-access:manage" | "audit:read" | "fleet:read";
397
+ interface TeamRecord {
398
+ id: string;
399
+ name: string;
400
+ authorizationEnabled: boolean;
401
+ revision: number;
402
+ createdAt: number;
403
+ updatedAt: number;
404
+ }
405
+ interface TeamMember {
406
+ id: string;
407
+ displayName: string;
408
+ kind: TeamMemberKind;
409
+ status: TeamMemberStatus;
410
+ revision: number;
411
+ createdAt: number;
412
+ updatedAt: number;
413
+ }
414
+ interface TeamRoleBinding {
415
+ id: string;
416
+ memberId: string;
417
+ role: TeamRole;
418
+ scopeType: TeamScopeType;
419
+ scopeId?: string;
420
+ createdAt: number;
421
+ }
422
+ interface TeamPrincipalBinding {
423
+ actorType: TeamPrincipalType;
424
+ actorId: string;
425
+ memberId: string;
426
+ createdAt: number;
427
+ }
428
+ interface TeamAuthorizationResource {
429
+ hostId?: string;
430
+ workspaceId?: string;
431
+ }
432
+ interface TeamAuthorizationDecision {
433
+ allowed: boolean;
434
+ reason: "local-break-glass" | "not-enforced" | "unbound" | "inactive" | "role" | "missing-permission";
435
+ member?: TeamMember;
436
+ roles: TeamRole[];
437
+ }
438
+ interface TeamStore {
439
+ readonly mode: TeamStoreMode;
440
+ getTeam(): TeamRecord | null;
441
+ createTeam(input: {
442
+ name: string;
443
+ ownerName: string;
444
+ ownerPrincipal: {
445
+ actorType: TeamPrincipalType;
446
+ actorId: string;
447
+ };
448
+ }, now?: number): {
449
+ team: TeamRecord;
450
+ owner: TeamMember;
451
+ };
452
+ updateTeam(input: {
453
+ name?: string;
454
+ authorizationEnabled?: boolean;
455
+ }, expectedRevision: number, now?: number): TeamRecord;
456
+ listMembers(opts?: {
457
+ includeRemoved?: boolean;
458
+ }): TeamMember[];
459
+ getMember(id: string): TeamMember | undefined;
460
+ createMember(input: {
461
+ displayName: string;
462
+ kind?: TeamMemberKind;
463
+ }, now?: number): TeamMember;
464
+ updateMember(id: string, input: {
465
+ displayName?: string;
466
+ status?: TeamMemberStatus;
467
+ }, expectedRevision: number, now?: number): TeamMember | undefined;
468
+ listRoleBindings(memberId?: string): TeamRoleBinding[];
469
+ grantRole(input: {
470
+ memberId: string;
471
+ role: TeamRole;
472
+ scopeType?: TeamScopeType;
473
+ scopeId?: string;
474
+ }, now?: number): TeamRoleBinding;
475
+ setNodeAccessRole(input: {
476
+ memberId: string;
477
+ nodeId: string;
478
+ role: "viewer" | "operator" | "node-admin";
479
+ }, now?: number): TeamRoleBinding;
480
+ revokeRole(id: string, now?: number): boolean;
481
+ listPrincipalBindings(memberId?: string): TeamPrincipalBinding[];
482
+ bindPrincipal(input: {
483
+ memberId: string;
484
+ actorType: TeamPrincipalType;
485
+ actorId: string;
486
+ }, now?: number): TeamPrincipalBinding;
487
+ unbindPrincipal(actorType: TeamPrincipalType, actorId: string, now?: number): boolean;
488
+ memberForPrincipal(actorType: TeamPrincipalType, actorId: string): TeamMember | undefined;
489
+ authorize(actorType: TeamPrincipalType, actorId: string, permission: TeamPermission, resource?: TeamAuthorizationResource): TeamAuthorizationDecision;
490
+ close(): void;
491
+ }
492
+
493
+ type CloudAuthorizationSnapshotStatus = "unavailable" | "pending" | "active" | "expired";
494
+ interface CloudAuthorizationState {
495
+ status: CloudAuthorizationSnapshotStatus;
496
+ revision?: number;
497
+ notBefore?: number;
498
+ expiresAt?: number;
499
+ }
500
+
372
501
  declare global {
373
502
  /** Stable package version injected by tsup. Source/test builds fall back to package.json. */
374
503
  const __SERVER_VERSION__: string | undefined;
@@ -689,6 +818,17 @@ declare class TerminalManager {
689
818
  private cleanupRecordPaths;
690
819
  private applyRuntimeSignal;
691
820
  write(id: string, data: string): void;
821
+ /**
822
+ * Start a newly-created automation Session and submit its task exactly once. `create()` is intentionally
823
+ * lazy, so writing before an attach would be a silent no-op; this method first owns a private attachment,
824
+ * waits for the provider TUI's idle composer (not merely a banner/auth/trust frame), then sends one bracketed
825
+ * paste and releases the attachment.
826
+ * The task never enters argv, logs, audit metadata, or a WebSocket URL.
827
+ */
828
+ bootstrapTask(id: string, task: string, options?: {
829
+ readyTimeoutMs?: number;
830
+ signal?: AbortSignal;
831
+ }): Promise<void>;
692
832
  resize(id: string, cols: number, rows: number): void;
693
833
  stop(id: string): void;
694
834
  get(id: string): TerminalMeta | undefined;
@@ -788,102 +928,27 @@ declare class InputLeaseCoordinator {
788
928
  private emit;
789
929
  }
790
930
 
791
- type TeamStoreMode = "sqlite" | "memory-fallback";
792
- type TeamMemberKind = "person" | "service";
793
- type TeamMemberStatus = "active" | "suspended" | "removed";
794
- type TeamRole = "viewer" | "operator" | "workspace-manager" | "extension-manager" | "policy-admin" | "organization-admin";
795
- type TeamScopeType = "team" | "host" | "workspace";
796
- type TeamPrincipalType = "device" | "host" | "local" | "relay";
797
- type TeamPermission = "team:read" | "sessions:read" | "sessions:operate" | "attention:read" | "attention:manage" | "presence:read" | "presence:write" | "workspaces:manage" | "extensions:manage" | "policy:manage" | "members:manage" | "audit:read" | "fleet:read";
798
- interface TeamRecord {
799
- id: string;
800
- name: string;
801
- authorizationEnabled: boolean;
802
- revision: number;
803
- createdAt: number;
804
- updatedAt: number;
805
- }
806
- interface TeamMember {
807
- id: string;
808
- displayName: string;
809
- kind: TeamMemberKind;
810
- status: TeamMemberStatus;
811
- revision: number;
812
- createdAt: number;
813
- updatedAt: number;
814
- }
815
- interface TeamRoleBinding {
816
- id: string;
817
- memberId: string;
818
- role: TeamRole;
819
- scopeType: TeamScopeType;
820
- scopeId?: string;
821
- createdAt: number;
822
- }
823
- interface TeamPrincipalBinding {
824
- actorType: TeamPrincipalType;
825
- actorId: string;
826
- memberId: string;
827
- createdAt: number;
828
- }
829
- interface TeamAuthorizationResource {
830
- hostId?: string;
831
- workspaceId?: string;
832
- }
833
- interface TeamAuthorizationDecision {
834
- allowed: boolean;
835
- reason: "local-break-glass" | "not-enforced" | "unbound" | "inactive" | "role" | "missing-permission";
836
- member?: TeamMember;
837
- roles: TeamRole[];
838
- }
839
- interface TeamStore {
840
- readonly mode: TeamStoreMode;
841
- getTeam(): TeamRecord | null;
842
- createTeam(input: {
843
- name: string;
844
- ownerName: string;
845
- ownerPrincipal: {
846
- actorType: TeamPrincipalType;
847
- actorId: string;
848
- };
849
- }, now?: number): {
850
- team: TeamRecord;
851
- owner: TeamMember;
852
- };
853
- updateTeam(input: {
854
- name?: string;
855
- authorizationEnabled?: boolean;
856
- }, expectedRevision: number, now?: number): TeamRecord;
857
- listMembers(opts?: {
858
- includeRemoved?: boolean;
859
- }): TeamMember[];
860
- getMember(id: string): TeamMember | undefined;
861
- createMember(input: {
862
- displayName: string;
863
- kind?: TeamMemberKind;
864
- }, now?: number): TeamMember;
865
- updateMember(id: string, input: {
866
- displayName?: string;
867
- status?: TeamMemberStatus;
868
- }, expectedRevision: number, now?: number): TeamMember | undefined;
869
- listRoleBindings(memberId?: string): TeamRoleBinding[];
870
- grantRole(input: {
871
- memberId: string;
872
- role: TeamRole;
873
- scopeType?: TeamScopeType;
874
- scopeId?: string;
875
- }, now?: number): TeamRoleBinding;
876
- revokeRole(id: string, now?: number): boolean;
877
- listPrincipalBindings(memberId?: string): TeamPrincipalBinding[];
878
- bindPrincipal(input: {
879
- memberId: string;
880
- actorType: TeamPrincipalType;
881
- actorId: string;
882
- }, now?: number): TeamPrincipalBinding;
883
- unbindPrincipal(actorType: TeamPrincipalType, actorId: string, now?: number): boolean;
884
- memberForPrincipal(actorType: TeamPrincipalType, actorId: string): TeamMember | undefined;
885
- authorize(actorType: TeamPrincipalType, actorId: string, permission: TeamPermission, resource?: TeamAuthorizationResource): TeamAuthorizationDecision;
886
- close(): void;
931
+ interface CloudHostRuntimeStatus {
932
+ running: boolean;
933
+ heartbeatFailures: number;
934
+ authorizationFailures: number;
935
+ automationFailures: number;
936
+ authorizationIssue?: CloudHostAuthorizationIssue;
937
+ lastHeartbeatAt?: number;
938
+ lastAuthorizationAt?: number;
939
+ authorization: CloudAuthorizationState;
940
+ }
941
+ type CloudHostAuthorizationIssue = "connectivity" | "credential-rejected" | "invalid-control-plane-response" | "authorization-verification-failed" | "trust-expired";
942
+ interface CloudHostRuntime {
943
+ start(): void;
944
+ stop(): Promise<void>;
945
+ sendHeartbeat(state?: "ready" | "draining"): Promise<void>;
946
+ refreshAuthorizationKeyset(): Promise<boolean>;
947
+ refreshAuthorizationSnapshot(): Promise<number>;
948
+ syncAuthorization(): Promise<number>;
949
+ /** Waits out an older in-flight poll, then starts a snapshot request from the resulting durable revision. */
950
+ syncAuthorizationFresh(): Promise<number>;
951
+ status(): CloudHostRuntimeStatus;
887
952
  }
888
953
 
889
954
  type PolicyStoreMode = "sqlite" | "memory-fallback";
@@ -1078,6 +1143,10 @@ interface CreateServerResult {
1078
1143
  presence: PresenceCoordinator;
1079
1144
  /** False when tmux/node-pty is unavailable → terminal sessions are disabled (startServer warns loudly). */
1080
1145
  terminalAvailable: boolean;
1146
+ /** Privacy-bounded webhook routing records synchronized to an optional managed control plane. */
1147
+ automationWebhookRegistrations(): CloudAutomationWebhookRegistration[];
1148
+ /** Durably accepts one control-plane signal; the invocation id makes redelivery idempotent. */
1149
+ acceptCloudAutomationInvocation(invocation: CloudAutomationInvocation): Promise<void>;
1081
1150
  }
1082
1151
 
1083
1152
  type RelayHostStatus = "idle" | "connecting" | "online" | "reconnecting" | "stopped";
@@ -1121,11 +1190,22 @@ declare function claudePreflightWarning(availability: ClaudeAvailability): strin
1121
1190
  * (the probe is short-timeout-guarded). Injectable probe + log sink so it's testable without a spawn.
1122
1191
  */
1123
1192
  declare function runClaudePreflight(probe: ClaudeVersionProbe, warn?: (msg: string) => void): Promise<void>;
1124
- declare function startServer(env?: NodeJS.ProcessEnv): Promise<CreateServerResult & {
1193
+ interface StartServerOptions {
1194
+ /** Shared control-plane transport seam for isolated embedding and startup tests. */
1195
+ fetch?: typeof globalThis.fetch;
1196
+ }
1197
+ declare function cloudHostCapabilities(input: {
1198
+ authorizationVersion: number;
1199
+ terminalAvailable: boolean;
1200
+ relayEnabled: boolean;
1201
+ managedDeviceEnrollmentEnabled: boolean;
1202
+ }): string[];
1203
+ declare function startServer(env?: NodeJS.ProcessEnv, options?: StartServerOptions): Promise<CreateServerResult & {
1125
1204
  url: string;
1126
1205
  token?: string;
1127
1206
  tokenGenerated: boolean;
1128
1207
  relayHost?: RelayHostConnector;
1208
+ cloudHostRuntime?: CloudHostRuntime;
1129
1209
  }>;
1130
1210
 
1131
- export { claudePreflightWarning, providerPreflightWarning, runClaudePreflight, runProviderPreflight, startServer };
1211
+ export { type StartServerOptions, claudePreflightWarning, cloudHostCapabilities, providerPreflightWarning, runClaudePreflight, runProviderPreflight, startServer };