@generacy-ai/control-plane 0.0.0-preview-20260507200146

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.
Files changed (87) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +66 -0
  3. package/dist/bin/control-plane.d.ts +3 -0
  4. package/dist/bin/control-plane.d.ts.map +1 -0
  5. package/dist/bin/control-plane.js +36 -0
  6. package/dist/bin/control-plane.js.map +1 -0
  7. package/dist/src/context.d.ts +8 -0
  8. package/dist/src/context.d.ts.map +1 -0
  9. package/dist/src/context.js +20 -0
  10. package/dist/src/context.js.map +1 -0
  11. package/dist/src/errors.d.ts +16 -0
  12. package/dist/src/errors.d.ts.map +1 -0
  13. package/dist/src/errors.js +37 -0
  14. package/dist/src/errors.js.map +1 -0
  15. package/dist/src/index.d.ts +9 -0
  16. package/dist/src/index.d.ts.map +1 -0
  17. package/dist/src/index.js +12 -0
  18. package/dist/src/index.js.map +1 -0
  19. package/dist/src/relay-events.d.ts +5 -0
  20. package/dist/src/relay-events.d.ts.map +1 -0
  21. package/dist/src/relay-events.js +9 -0
  22. package/dist/src/relay-events.js.map +1 -0
  23. package/dist/src/router.d.ts +4 -0
  24. package/dist/src/router.d.ts.map +1 -0
  25. package/dist/src/router.js +83 -0
  26. package/dist/src/router.js.map +1 -0
  27. package/dist/src/routes/audit.d.ts +10 -0
  28. package/dist/src/routes/audit.d.ts.map +1 -0
  29. package/dist/src/routes/audit.js +38 -0
  30. package/dist/src/routes/audit.js.map +1 -0
  31. package/dist/src/routes/credentials.d.ts +5 -0
  32. package/dist/src/routes/credentials.d.ts.map +1 -0
  33. package/dist/src/routes/credentials.js +23 -0
  34. package/dist/src/routes/credentials.js.map +1 -0
  35. package/dist/src/routes/lifecycle.d.ts +4 -0
  36. package/dist/src/routes/lifecycle.d.ts.map +1 -0
  37. package/dist/src/routes/lifecycle.js +80 -0
  38. package/dist/src/routes/lifecycle.js.map +1 -0
  39. package/dist/src/routes/roles.d.ts +5 -0
  40. package/dist/src/routes/roles.d.ts.map +1 -0
  41. package/dist/src/routes/roles.js +20 -0
  42. package/dist/src/routes/roles.js.map +1 -0
  43. package/dist/src/routes/state.d.ts +4 -0
  44. package/dist/src/routes/state.d.ts.map +1 -0
  45. package/dist/src/routes/state.js +17 -0
  46. package/dist/src/routes/state.js.map +1 -0
  47. package/dist/src/routes/status.d.ts +4 -0
  48. package/dist/src/routes/status.d.ts.map +1 -0
  49. package/dist/src/routes/status.js +29 -0
  50. package/dist/src/routes/status.js.map +1 -0
  51. package/dist/src/schemas.d.ts +314 -0
  52. package/dist/src/schemas.d.ts.map +1 -0
  53. package/dist/src/schemas.js +94 -0
  54. package/dist/src/schemas.js.map +1 -0
  55. package/dist/src/server.d.ts +9 -0
  56. package/dist/src/server.d.ts.map +1 -0
  57. package/dist/src/server.js +70 -0
  58. package/dist/src/server.js.map +1 -0
  59. package/dist/src/services/code-server-manager.d.ts +44 -0
  60. package/dist/src/services/code-server-manager.d.ts.map +1 -0
  61. package/dist/src/services/code-server-manager.js +164 -0
  62. package/dist/src/services/code-server-manager.js.map +1 -0
  63. package/dist/src/services/default-role-writer.d.ts +7 -0
  64. package/dist/src/services/default-role-writer.d.ts.map +1 -0
  65. package/dist/src/services/default-role-writer.js +44 -0
  66. package/dist/src/services/default-role-writer.js.map +1 -0
  67. package/dist/src/services/peer-repo-cloner.d.ts +12 -0
  68. package/dist/src/services/peer-repo-cloner.d.ts.map +1 -0
  69. package/dist/src/services/peer-repo-cloner.js +80 -0
  70. package/dist/src/services/peer-repo-cloner.js.map +1 -0
  71. package/dist/src/services/tunnel-handler.d.ts +30 -0
  72. package/dist/src/services/tunnel-handler.d.ts.map +1 -0
  73. package/dist/src/services/tunnel-handler.js +110 -0
  74. package/dist/src/services/tunnel-handler.js.map +1 -0
  75. package/dist/src/state.d.ts +8 -0
  76. package/dist/src/state.d.ts.map +1 -0
  77. package/dist/src/state.js +39 -0
  78. package/dist/src/state.js.map +1 -0
  79. package/dist/src/types.d.ts +12 -0
  80. package/dist/src/types.d.ts.map +1 -0
  81. package/dist/src/types.js +2 -0
  82. package/dist/src/types.js.map +1 -0
  83. package/dist/src/util/read-body.d.ts +3 -0
  84. package/dist/src/util/read-body.d.ts.map +1 -0
  85. package/dist/src/util/read-body.js +9 -0
  86. package/dist/src/util/read-body.js.map +1 -0
  87. package/package.json +51 -0
@@ -0,0 +1,94 @@
1
+ import { z } from 'zod';
2
+ // Re-export credential/role schemas from @generacy-ai/credhelper
3
+ export { CredentialEntrySchema, RoleConfigSchema, RoleCredentialRefSchema, } from '@generacy-ai/credhelper';
4
+ // Cluster state enums
5
+ export const ClusterStatusSchema = z.enum(['bootstrapping', 'ready', 'degraded', 'error']);
6
+ export const DeploymentModeSchema = z.enum(['local', 'cloud']);
7
+ export const ClusterVariantSchema = z.enum(['cluster-base', 'cluster-microservices']);
8
+ export const ClusterStateSchema = z.object({
9
+ status: ClusterStatusSchema,
10
+ deploymentMode: DeploymentModeSchema,
11
+ variant: ClusterVariantSchema,
12
+ lastSeen: z.string().datetime(),
13
+ statusReason: z.string().max(200).optional(),
14
+ });
15
+ export const StatusUpdateSchema = z.object({
16
+ status: ClusterStatusSchema,
17
+ statusReason: z.string().max(200).optional(),
18
+ });
19
+ // Lifecycle
20
+ export const LifecycleActionSchema = z.enum([
21
+ 'clone-peer-repos',
22
+ 'set-default-role',
23
+ 'code-server-start',
24
+ 'code-server-stop',
25
+ 'stop',
26
+ ]);
27
+ export const ClonePeerReposBodySchema = z.object({
28
+ repos: z.array(z.string().url()).min(0),
29
+ token: z.string().optional(),
30
+ });
31
+ export const SetDefaultRoleBodySchema = z.object({
32
+ role: z.string().min(1).max(64).regex(/^[a-z0-9-]+$/),
33
+ });
34
+ export const LifecycleResponseSchema = z.object({
35
+ accepted: z.literal(true),
36
+ action: LifecycleActionSchema,
37
+ });
38
+ // code-server-start returns its own runtime status + socket path so the caller
39
+ // (typically the cloud UI proxying through the relay) can connect immediately.
40
+ export const CodeServerStartResponseSchema = z.object({
41
+ status: z.enum(['starting', 'running']),
42
+ socket_path: z.string(),
43
+ });
44
+ // Credential stub response (wraps entry with runtime status)
45
+ export const CredentialStubResponseSchema = z.object({
46
+ id: z.string(),
47
+ type: z.string(),
48
+ backend: z.string(),
49
+ backendKey: z.string(),
50
+ status: z.enum(['active', 'pending', 'error']),
51
+ createdAt: z.string().datetime(),
52
+ });
53
+ // Audit schemas (mirrored from credhelper-daemon for validation)
54
+ export const AuditActionSchema = z.enum([
55
+ 'session.begin',
56
+ 'session.end',
57
+ 'credential.mint',
58
+ 'credential.resolve',
59
+ 'credential.refresh',
60
+ 'exposure.render',
61
+ 'proxy.docker',
62
+ 'proxy.localhost',
63
+ ]);
64
+ export const AuditEntrySchema = z.object({
65
+ timestamp: z.string(),
66
+ action: AuditActionSchema,
67
+ actor: z.object({
68
+ workerId: z.string(),
69
+ sessionId: z.string().optional(),
70
+ }),
71
+ clusterId: z.string(),
72
+ credentialId: z.string().optional(),
73
+ role: z.string().optional(),
74
+ pluginId: z.string().optional(),
75
+ success: z.boolean(),
76
+ errorCode: z.string().optional(),
77
+ exposureKind: z.string().optional(),
78
+ proxy: z.object({
79
+ method: z.string(),
80
+ path: z.string(),
81
+ decision: z.enum(['allow', 'deny']),
82
+ }).optional(),
83
+ });
84
+ export const AuditBatchSchema = z.object({
85
+ entries: z.array(AuditEntrySchema).max(50),
86
+ droppedSinceLastBatch: z.number().int().min(0),
87
+ });
88
+ // Error response
89
+ export const ErrorResponseSchema = z.object({
90
+ error: z.string(),
91
+ code: z.string(),
92
+ details: z.unknown().optional(),
93
+ });
94
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare class ControlPlaneServer {
2
+ private server;
3
+ private socketPath;
4
+ constructor();
5
+ private handleRequest;
6
+ start(socketPath: string): Promise<void>;
7
+ close(): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAQA,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,UAAU,CAAqB;;YAoBzB,aAAa;IAQrB,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAmBvB"}
@@ -0,0 +1,70 @@
1
+ import http from 'node:http';
2
+ import fs from 'node:fs/promises';
3
+ import { extractActorContext } from './context.js';
4
+ import { ControlPlaneError, sendError } from './errors.js';
5
+ import { dispatch } from './router.js';
6
+ import { getCodeServerManager } from './services/code-server-manager.js';
7
+ export class ControlPlaneServer {
8
+ server;
9
+ socketPath;
10
+ constructor() {
11
+ this.server = http.createServer((req, res) => {
12
+ this.handleRequest(req, res).catch((err) => {
13
+ if (err instanceof ControlPlaneError) {
14
+ sendError(res, err);
15
+ }
16
+ else {
17
+ sendError(res, new ControlPlaneError('INTERNAL_ERROR', err instanceof Error ? err.message : 'Internal error'));
18
+ }
19
+ });
20
+ });
21
+ }
22
+ async handleRequest(req, res) {
23
+ const actor = extractActorContext(req);
24
+ await dispatch(req, res, actor);
25
+ }
26
+ async start(socketPath) {
27
+ this.socketPath = socketPath;
28
+ // Remove stale socket file if exists
29
+ try {
30
+ await fs.unlink(socketPath);
31
+ }
32
+ catch {
33
+ // Ignore ENOENT
34
+ }
35
+ return new Promise((resolve, reject) => {
36
+ this.server.on('error', reject);
37
+ this.server.listen(socketPath, async () => {
38
+ try {
39
+ await fs.chmod(socketPath, 0o660);
40
+ }
41
+ catch {
42
+ // Best-effort chmod
43
+ }
44
+ resolve();
45
+ });
46
+ });
47
+ }
48
+ close() {
49
+ return new Promise((resolve) => {
50
+ this.server.close(async () => {
51
+ try {
52
+ await getCodeServerManager().shutdown();
53
+ }
54
+ catch {
55
+ // Best-effort: don't block server shutdown on a wedged code-server child
56
+ }
57
+ if (this.socketPath) {
58
+ try {
59
+ await fs.unlink(this.socketPath);
60
+ }
61
+ catch {
62
+ // Ignore ENOENT
63
+ }
64
+ }
65
+ resolve();
66
+ });
67
+ });
68
+ }
69
+ }
70
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,MAAM,OAAO,kBAAkB;IACrB,MAAM,CAAc;IACpB,UAAU,CAAqB;IAEvC;QACE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC3C,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACzC,IAAI,GAAG,YAAY,iBAAiB,EAAE,CAAC;oBACrC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBACtB,CAAC;qBAAM,CAAC;oBACN,SAAS,CACP,GAAG,EACH,IAAI,iBAAiB,CACnB,gBAAgB,EAChB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CACtD,CACF,CAAC;gBACJ,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,aAAa,CACzB,GAAyB,EACzB,GAAwB;QAExB,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,UAAkB;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAE7B,qCAAqC;QACrC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,gBAAgB;QAClB,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;gBACxC,IAAI,CAAC;oBACH,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBACpC,CAAC;gBAAC,MAAM,CAAC;oBACP,oBAAoB;gBACtB,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE;gBAC3B,IAAI,CAAC;oBACH,MAAM,oBAAoB,EAAE,CAAC,QAAQ,EAAE,CAAC;gBAC1C,CAAC;gBAAC,MAAM,CAAC;oBACP,yEAAyE;gBAC3E,CAAC;gBACD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACpB,IAAI,CAAC;wBACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACnC,CAAC;oBAAC,MAAM,CAAC;wBACP,gBAAgB;oBAClB,CAAC;gBACH,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ export type CodeServerStatus = 'stopped' | 'starting' | 'running';
2
+ export interface CodeServerStartResult {
3
+ status: 'starting' | 'running';
4
+ socket_path: string;
5
+ }
6
+ export interface CodeServerManager {
7
+ start(): Promise<CodeServerStartResult>;
8
+ stop(): Promise<void>;
9
+ touch(): void;
10
+ getStatus(): CodeServerStatus;
11
+ shutdown(): Promise<void>;
12
+ }
13
+ export interface CodeServerManagerOptions {
14
+ binPath: string;
15
+ socketPath: string;
16
+ idleTimeoutMs: number;
17
+ userDataDir?: string;
18
+ extensionsDir?: string;
19
+ forceKillTimeoutMs?: number;
20
+ }
21
+ export declare const DEFAULT_CODE_SERVER_BIN = "/usr/local/bin/code-server";
22
+ export declare const DEFAULT_CODE_SERVER_SOCKET = "/run/code-server.sock";
23
+ export declare const DEFAULT_IDLE_TIMEOUT_MS: number;
24
+ export declare function loadOptionsFromEnv(env?: NodeJS.ProcessEnv): CodeServerManagerOptions;
25
+ export declare class CodeServerProcessManager implements CodeServerManager {
26
+ private readonly opts;
27
+ private child;
28
+ private status;
29
+ private idleTimer;
30
+ private exitWaiters;
31
+ constructor(opts: CodeServerManagerOptions);
32
+ getStatus(): CodeServerStatus;
33
+ start(): Promise<CodeServerStartResult>;
34
+ stop(): Promise<void>;
35
+ touch(): void;
36
+ shutdown(): Promise<void>;
37
+ private clearIdleTimer;
38
+ private ensureSocketDir;
39
+ private removeStaleSocket;
40
+ private waitForSocket;
41
+ }
42
+ export declare function getCodeServerManager(): CodeServerManager;
43
+ export declare function setCodeServerManager(next: CodeServerManager | null): void;
44
+ //# sourceMappingURL=code-server-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-server-manager.d.ts","sourceRoot":"","sources":["../../../src/services/code-server-manager.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAElE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACxC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,IAAI,CAAC;IACd,SAAS,IAAI,gBAAgB,CAAC;IAC9B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,uBAAuB,+BAA+B,CAAC;AACpE,eAAO,MAAM,0BAA0B,0BAA0B,CAAC;AAClE,eAAO,MAAM,uBAAuB,QAAiB,CAAC;AAEtD,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,wBAAwB,CAajG;AAED,qBAAa,wBAAyB,YAAW,iBAAiB;IAMpD,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,WAAW,CAAyB;gBAEf,IAAI,EAAE,wBAAwB;IAE3D,SAAS,IAAI,gBAAgB;IAIvB,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAwDvC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,KAAK,IAAI,IAAI;IASP,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,OAAO,CAAC,cAAc;YAOR,eAAe;YASf,iBAAiB;YAQjB,aAAa;CAa5B;AAID,wBAAgB,oBAAoB,IAAI,iBAAiB,CAKxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAEzE"}
@@ -0,0 +1,164 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ export const DEFAULT_CODE_SERVER_BIN = '/usr/local/bin/code-server';
5
+ export const DEFAULT_CODE_SERVER_SOCKET = '/run/code-server.sock';
6
+ export const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
7
+ export function loadOptionsFromEnv(env = process.env) {
8
+ const idleRaw = env['CODE_SERVER_IDLE_TIMEOUT_MS'];
9
+ const idleTimeoutMs = idleRaw ? Number.parseInt(idleRaw, 10) : DEFAULT_IDLE_TIMEOUT_MS;
10
+ const opts = {
11
+ binPath: env['CODE_SERVER_BIN'] ?? DEFAULT_CODE_SERVER_BIN,
12
+ socketPath: env['CODE_SERVER_SOCKET_PATH'] ?? DEFAULT_CODE_SERVER_SOCKET,
13
+ idleTimeoutMs: Number.isFinite(idleTimeoutMs) && idleTimeoutMs > 0 ? idleTimeoutMs : DEFAULT_IDLE_TIMEOUT_MS,
14
+ };
15
+ if (env['CODE_SERVER_USER_DATA_DIR'])
16
+ opts.userDataDir = env['CODE_SERVER_USER_DATA_DIR'];
17
+ if (env['CODE_SERVER_EXTENSIONS_DIR'])
18
+ opts.extensionsDir = env['CODE_SERVER_EXTENSIONS_DIR'];
19
+ return opts;
20
+ }
21
+ export class CodeServerProcessManager {
22
+ opts;
23
+ child = null;
24
+ status = 'stopped';
25
+ idleTimer = null;
26
+ exitWaiters = [];
27
+ constructor(opts) {
28
+ this.opts = opts;
29
+ }
30
+ getStatus() {
31
+ return this.status;
32
+ }
33
+ async start() {
34
+ if (this.child) {
35
+ this.touch();
36
+ return { status: this.status === 'running' ? 'running' : 'starting', socket_path: this.opts.socketPath };
37
+ }
38
+ await this.ensureSocketDir();
39
+ await this.removeStaleSocket();
40
+ const args = [
41
+ '--socket', this.opts.socketPath,
42
+ '--socket-mode', '0660',
43
+ '--auth', 'none',
44
+ '--disable-telemetry',
45
+ '--disable-update-check',
46
+ ];
47
+ if (this.opts.userDataDir)
48
+ args.push('--user-data-dir', this.opts.userDataDir);
49
+ if (this.opts.extensionsDir)
50
+ args.push('--extensions-dir', this.opts.extensionsDir);
51
+ this.status = 'starting';
52
+ const child = spawn(this.opts.binPath, args, {
53
+ stdio: ['ignore', 'pipe', 'pipe'],
54
+ detached: false,
55
+ });
56
+ child.on('exit', () => {
57
+ this.child = null;
58
+ this.status = 'stopped';
59
+ this.clearIdleTimer();
60
+ const waiters = this.exitWaiters;
61
+ this.exitWaiters = [];
62
+ for (const w of waiters)
63
+ w();
64
+ });
65
+ child.on('error', () => {
66
+ this.child = null;
67
+ this.status = 'stopped';
68
+ this.clearIdleTimer();
69
+ });
70
+ this.child = child;
71
+ // Mark running once the socket appears (best-effort) and start the idle timer.
72
+ this.waitForSocket().then(() => {
73
+ if (this.child === child)
74
+ this.status = 'running';
75
+ }, () => {
76
+ // socket never appeared; leave status as 'starting' until exit cleans up
77
+ });
78
+ this.touch();
79
+ return { status: 'starting', socket_path: this.opts.socketPath };
80
+ }
81
+ async stop() {
82
+ this.clearIdleTimer();
83
+ const child = this.child;
84
+ if (!child)
85
+ return;
86
+ return new Promise((resolve) => {
87
+ this.exitWaiters.push(resolve);
88
+ child.kill('SIGTERM');
89
+ const forceKill = setTimeout(() => {
90
+ if (this.child === child) {
91
+ try {
92
+ child.kill('SIGKILL');
93
+ }
94
+ catch {
95
+ // already gone
96
+ }
97
+ }
98
+ }, this.opts.forceKillTimeoutMs ?? 5000);
99
+ child.once('exit', () => clearTimeout(forceKill));
100
+ });
101
+ }
102
+ touch() {
103
+ if (!this.child)
104
+ return;
105
+ this.clearIdleTimer();
106
+ this.idleTimer = setTimeout(() => {
107
+ void this.stop();
108
+ }, this.opts.idleTimeoutMs);
109
+ if (typeof this.idleTimer.unref === 'function')
110
+ this.idleTimer.unref();
111
+ }
112
+ async shutdown() {
113
+ await this.stop();
114
+ }
115
+ clearIdleTimer() {
116
+ if (this.idleTimer) {
117
+ clearTimeout(this.idleTimer);
118
+ this.idleTimer = null;
119
+ }
120
+ }
121
+ async ensureSocketDir() {
122
+ const dir = path.dirname(this.opts.socketPath);
123
+ try {
124
+ await fs.mkdir(dir, { recursive: true });
125
+ }
126
+ catch {
127
+ // best-effort; spawn will fail loudly if the dir is unusable
128
+ }
129
+ }
130
+ async removeStaleSocket() {
131
+ try {
132
+ await fs.unlink(this.opts.socketPath);
133
+ }
134
+ catch {
135
+ // ENOENT is fine; other errors surface when the child tries to bind
136
+ }
137
+ }
138
+ async waitForSocket(timeoutMs = 10_000) {
139
+ const deadline = Date.now() + timeoutMs;
140
+ while (Date.now() < deadline) {
141
+ if (!this.child)
142
+ throw new Error('code-server exited before socket appeared');
143
+ try {
144
+ await fs.stat(this.opts.socketPath);
145
+ return;
146
+ }
147
+ catch {
148
+ await new Promise((r) => setTimeout(r, 100));
149
+ }
150
+ }
151
+ throw new Error(`code-server socket did not appear within ${timeoutMs}ms`);
152
+ }
153
+ }
154
+ let manager = null;
155
+ export function getCodeServerManager() {
156
+ if (!manager) {
157
+ manager = new CodeServerProcessManager(loadOptionsFromEnv());
158
+ }
159
+ return manager;
160
+ }
161
+ export function setCodeServerManager(next) {
162
+ manager = next;
163
+ }
164
+ //# sourceMappingURL=code-server-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-server-manager.js","sourceRoot":"","sources":["../../../src/services/code-server-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface SetDefaultRoleOptions {
2
+ role: string;
3
+ agencyDir?: string;
4
+ configPath?: string;
5
+ }
6
+ export declare function setDefaultRole(options: SetDefaultRoleOptions): Promise<void>;
7
+ //# sourceMappingURL=default-role-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-role-writer.d.ts","sourceRoot":"","sources":["../../../src/services/default-role-writer.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4ClF"}
@@ -0,0 +1,44 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import YAML from 'yaml';
4
+ import { ControlPlaneError } from '../errors.js';
5
+ export async function setDefaultRole(options) {
6
+ const agencyDir = options.agencyDir ?? '.agency';
7
+ const configPath = options.configPath ?? '.generacy/config.yaml';
8
+ // Validate role file exists
9
+ const roleFilePath = path.join(agencyDir, 'roles', `${options.role}.yaml`);
10
+ try {
11
+ await fs.access(roleFilePath);
12
+ }
13
+ catch {
14
+ throw new ControlPlaneError('INVALID_REQUEST', `Role '${options.role}' not found: ${roleFilePath} does not exist`);
15
+ }
16
+ // Read existing config or start fresh
17
+ let doc = {};
18
+ try {
19
+ const existing = await fs.readFile(configPath, 'utf8');
20
+ const parsed = YAML.parse(existing);
21
+ if (parsed && typeof parsed === 'object') {
22
+ doc = parsed;
23
+ }
24
+ }
25
+ catch (err) {
26
+ if (err.code !== 'ENOENT') {
27
+ throw err;
28
+ }
29
+ // File doesn't exist yet — create it
30
+ }
31
+ // Set defaults.role
32
+ if (!doc.defaults || typeof doc.defaults !== 'object') {
33
+ doc.defaults = {};
34
+ }
35
+ doc.defaults.role = options.role;
36
+ // Ensure parent directory exists
37
+ const configDir = path.dirname(configPath);
38
+ await fs.mkdir(configDir, { recursive: true });
39
+ // Atomic write: temp + rename
40
+ const tmpPath = `${configPath}.tmp.${process.pid}`;
41
+ await fs.writeFile(tmpPath, YAML.stringify(doc), { mode: 0o644 });
42
+ await fs.rename(tmpPath, configPath);
43
+ }
44
+ //# sourceMappingURL=default-role-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-role-writer.js","sourceRoot":"","sources":["../../../src/services/default-role-writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAQjD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA8B;IACjE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,uBAAuB,CAAC;IAEjE,4BAA4B;IAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,iBAAiB,CACzB,iBAAiB,EACjB,SAAS,OAAO,CAAC,IAAI,gBAAgB,YAAY,iBAAiB,CACnE,CAAC;IACJ,CAAC;IAED,sCAAsC;IACtC,IAAI,GAAG,GAA4B,EAAE,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,GAAG,GAAG,MAAiC,CAAC;QAC1C,CAAC;IACH,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,qCAAqC;IACvC,CAAC;IAED,oBAAoB;IACpB,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC;IACpB,CAAC;IACA,GAAG,CAAC,QAAoC,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE9D,iCAAiC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,8BAA8B;IAC9B,MAAM,OAAO,GAAG,GAAG,UAAU,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IACnD,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface CloneResult {
2
+ repo: string;
3
+ status: 'done' | 'failed' | 'skipped';
4
+ message?: string;
5
+ }
6
+ export interface PeerRepoClonerOptions {
7
+ repos: string[];
8
+ token?: string;
9
+ workspacesDir?: string;
10
+ }
11
+ export declare function clonePeerRepos(options: PeerRepoClonerOptions): Promise<CloneResult[]>;
12
+ //# sourceMappingURL=peer-repo-cloner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-repo-cloner.d.ts","sourceRoot":"","sources":["../../../src/services/peer-repo-cloner.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA4CD,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA0C3F"}
@@ -0,0 +1,80 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { getRelayPushEvent } from '../relay-events.js';
5
+ function extractRepoName(repoUrl) {
6
+ return repoUrl.replace(/\.git$/, '').split('/').pop();
7
+ }
8
+ function buildCloneUrl(repo, token) {
9
+ if (!token)
10
+ return repo;
11
+ const url = new URL(repo);
12
+ url.username = 'x-access-token';
13
+ url.password = token;
14
+ return url.toString();
15
+ }
16
+ function emitBootstrapEvent(data) {
17
+ const pushEvent = getRelayPushEvent();
18
+ if (pushEvent) {
19
+ pushEvent('cluster.bootstrap', data);
20
+ }
21
+ }
22
+ function spawnClone(cloneUrl, targetDir) {
23
+ return new Promise((resolve, reject) => {
24
+ const child = spawn('git', ['clone', cloneUrl, targetDir], {
25
+ stdio: ['ignore', 'pipe', 'pipe'],
26
+ });
27
+ let stderr = '';
28
+ child.stderr?.on('data', (chunk) => {
29
+ stderr += chunk.toString();
30
+ });
31
+ child.on('close', (code) => {
32
+ if (code === 0) {
33
+ resolve();
34
+ }
35
+ else {
36
+ reject(new Error(stderr.trim() || `git clone exited with code ${code}`));
37
+ }
38
+ });
39
+ child.on('error', reject);
40
+ });
41
+ }
42
+ export async function clonePeerRepos(options) {
43
+ const workspacesDir = options.workspacesDir ?? '/workspaces';
44
+ const { repos, token } = options;
45
+ if (repos.length === 0) {
46
+ emitBootstrapEvent({ status: 'done', message: 'no peer repos' });
47
+ return [];
48
+ }
49
+ const results = [];
50
+ for (const repo of repos) {
51
+ const name = extractRepoName(repo);
52
+ const targetDir = path.join(workspacesDir, name);
53
+ // Idempotency: skip if directory already exists
54
+ try {
55
+ const stat = await fs.stat(targetDir);
56
+ if (stat.isDirectory()) {
57
+ emitBootstrapEvent({ repo, status: 'done' });
58
+ results.push({ repo, status: 'skipped' });
59
+ continue;
60
+ }
61
+ }
62
+ catch {
63
+ // Directory doesn't exist — proceed with clone
64
+ }
65
+ emitBootstrapEvent({ repo, status: 'cloning' });
66
+ try {
67
+ const cloneUrl = buildCloneUrl(repo, token);
68
+ await spawnClone(cloneUrl, targetDir);
69
+ emitBootstrapEvent({ repo, status: 'done' });
70
+ results.push({ repo, status: 'done' });
71
+ }
72
+ catch (err) {
73
+ const message = err instanceof Error ? err.message : 'Unknown error';
74
+ emitBootstrapEvent({ repo, status: 'failed', message });
75
+ results.push({ repo, status: 'failed', message });
76
+ }
77
+ }
78
+ return results;
79
+ }
80
+ //# sourceMappingURL=peer-repo-cloner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-repo-cloner.js","sourceRoot":"","sources":["../../../src/services/peer-repo-cloner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { CodeServerManager } from './code-server-manager.js';
2
+ export interface RelayMessageSender {
3
+ send(message: unknown): void;
4
+ }
5
+ interface TunnelOpenMessage {
6
+ tunnelId: string;
7
+ target: string;
8
+ }
9
+ interface TunnelDataMessage {
10
+ tunnelId: string;
11
+ data: string;
12
+ }
13
+ interface TunnelCloseMessage {
14
+ tunnelId: string;
15
+ reason?: string;
16
+ }
17
+ export declare class TunnelHandler {
18
+ private readonly relaySend;
19
+ private readonly codeServerManager;
20
+ private readonly allowedTarget;
21
+ private readonly tunnels;
22
+ constructor(relaySend: RelayMessageSender, codeServerManager: CodeServerManager, allowedTarget?: string);
23
+ handleOpen(msg: TunnelOpenMessage): Promise<void>;
24
+ handleData(msg: TunnelDataMessage): void;
25
+ handleClose(msg: TunnelCloseMessage): void;
26
+ cleanup(): void;
27
+ private connectWithTimeout;
28
+ }
29
+ export {};
30
+ //# sourceMappingURL=tunnel-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-handler.d.ts","sourceRoot":"","sources":["../../../src/services/tunnel-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,aAAa;IAItB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa;IALhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;gBAG3C,SAAS,EAAE,kBAAkB,EAC7B,iBAAiB,EAAE,iBAAiB,EACpC,aAAa,GAAE,MAAgC;IAG5D,UAAU,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkEvD,UAAU,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI;IAWxC,WAAW,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAQ1C,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,kBAAkB;CAkB3B"}