@termfleet/core 0.1.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.
Files changed (67) hide show
  1. package/dist/agent-launch.d.ts +78 -0
  2. package/dist/agent-launch.js +247 -0
  3. package/dist/agent-session-id.d.ts +10 -0
  4. package/dist/agent-session-id.js +36 -0
  5. package/dist/agent-session-index-client.d.ts +7 -0
  6. package/dist/agent-session-index-client.js +86 -0
  7. package/dist/agent-session-index-worker.d.ts +1 -0
  8. package/dist/agent-session-index-worker.js +20 -0
  9. package/dist/agent-session-index.d.ts +34 -0
  10. package/dist/agent-session-index.js +527 -0
  11. package/dist/agent-session-tail.d.ts +33 -0
  12. package/dist/agent-session-tail.js +184 -0
  13. package/dist/agent-session-watcher.d.ts +36 -0
  14. package/dist/agent-session-watcher.js +194 -0
  15. package/dist/agent-session.d.ts +380 -0
  16. package/dist/agent-session.js +1688 -0
  17. package/dist/background-runner.d.ts +3 -0
  18. package/dist/background-runner.js +55 -0
  19. package/dist/boot-queue.d.ts +35 -0
  20. package/dist/boot-queue.js +66 -0
  21. package/dist/build-info.d.ts +5 -0
  22. package/dist/build-info.js +38 -0
  23. package/dist/collab/canvas-doc.d.ts +47 -0
  24. package/dist/collab/canvas-doc.js +83 -0
  25. package/dist/contracts/auth.d.ts +77 -0
  26. package/dist/contracts/auth.js +1 -0
  27. package/dist/contracts/canvas.d.ts +34 -0
  28. package/dist/contracts/canvas.js +76 -0
  29. package/dist/contracts/console-layout.d.ts +39 -0
  30. package/dist/contracts/console-layout.js +135 -0
  31. package/dist/contracts/files.d.ts +38 -0
  32. package/dist/contracts/files.js +37 -0
  33. package/dist/contracts/provider-url.d.ts +3 -0
  34. package/dist/contracts/provider-url.js +49 -0
  35. package/dist/contracts/registry.d.ts +58 -0
  36. package/dist/contracts/registry.js +285 -0
  37. package/dist/launch-trace.d.ts +6 -0
  38. package/dist/launch-trace.js +33 -0
  39. package/dist/lib/errors.d.ts +1 -0
  40. package/dist/lib/errors.js +5 -0
  41. package/dist/lib/exec.d.ts +13 -0
  42. package/dist/lib/exec.js +134 -0
  43. package/dist/local-providers.d.ts +32 -0
  44. package/dist/local-providers.js +184 -0
  45. package/dist/local-tunnel.d.ts +6 -0
  46. package/dist/local-tunnel.js +258 -0
  47. package/dist/provider-access-token.d.ts +11 -0
  48. package/dist/provider-access-token.js +77 -0
  49. package/dist/provider-client.d.ts +152 -0
  50. package/dist/provider-client.js +666 -0
  51. package/dist/provider-url-resolver.d.ts +16 -0
  52. package/dist/provider-url-resolver.js +37 -0
  53. package/dist/registry-client.d.ts +93 -0
  54. package/dist/registry-client.js +170 -0
  55. package/dist/registry.d.ts +56 -0
  56. package/dist/registry.js +406 -0
  57. package/dist/session-attention.d.ts +24 -0
  58. package/dist/session-attention.js +54 -0
  59. package/dist/session-lifecycle.d.ts +83 -0
  60. package/dist/session-lifecycle.js +658 -0
  61. package/dist/session-window.d.ts +3 -0
  62. package/dist/session-window.js +20 -0
  63. package/dist/terminal-client.d.ts +49 -0
  64. package/dist/terminal-client.js +89 -0
  65. package/dist/types.d.ts +155 -0
  66. package/dist/types.js +21 -0
  67. package/package.json +26 -0
@@ -0,0 +1,37 @@
1
+ import { normalizeProviderOrigin } from "./contracts/provider-url.js";
2
+ export const providerProxyPathPrefix = "/providers/";
3
+ export function providerProxyKey(providerBaseUrl) {
4
+ return encodeURIComponent(normalizeProviderOrigin(providerBaseUrl));
5
+ }
6
+ export function providerProxyPath(providerBaseUrl) {
7
+ return `${providerProxyPathPrefix}${providerProxyKey(providerBaseUrl)}`;
8
+ }
9
+ export const directProviderUrlResolver = {
10
+ httpBase: (providerBaseUrl) => normalizeProviderOrigin(providerBaseUrl),
11
+ socketTarget: (providerBaseUrl) => ({
12
+ origin: normalizeProviderOrigin(providerBaseUrl),
13
+ path: "/control/socket.io"
14
+ }),
15
+ wsAuthQuery: () => ({}),
16
+ wsBase: (providerBaseUrl) => toWebSocketOrigin(normalizeProviderOrigin(providerBaseUrl))
17
+ };
18
+ export function consoleProxyProviderUrlResolver(consoleOrigin, options = {}) {
19
+ const origin = new URL(consoleOrigin).origin;
20
+ return {
21
+ httpBase: (providerBaseUrl) => `${origin}${providerProxyPath(providerBaseUrl)}`,
22
+ socketTarget: (providerBaseUrl) => ({
23
+ origin,
24
+ path: `${providerProxyPath(providerBaseUrl)}/control/socket.io`
25
+ }),
26
+ wsAuthQuery: () => {
27
+ const token = options.sessionToken?.();
28
+ return token ? { termfleet_session: token } : {};
29
+ },
30
+ wsBase: (providerBaseUrl) => `${toWebSocketOrigin(origin)}${providerProxyPath(providerBaseUrl)}`
31
+ };
32
+ }
33
+ function toWebSocketOrigin(httpOrigin) {
34
+ const url = new URL(httpOrigin);
35
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
36
+ return url.origin;
37
+ }
@@ -0,0 +1,93 @@
1
+ import type { AuthOrganizationId, AuthSessionResource, AuthSignInResponse } from "./contracts/auth.js";
2
+ import type { TerminalThemeProfile } from "./types.js";
3
+ import { type CanvasComment, type CanvasPoint } from "./contracts/canvas.js";
4
+ import { type ConsoleLayout } from "./contracts/console-layout.js";
5
+ import { type RegistryProviderRecord } from "./contracts/registry.js";
6
+ export type RegistryClientOptions = {
7
+ baseUrl: string;
8
+ };
9
+ export type ProviderAccessTokenResource = {
10
+ expireAt: number;
11
+ object: "provider_access_token";
12
+ providerBaseUrl: string;
13
+ token: string;
14
+ };
15
+ export declare class RegistryClient {
16
+ private readonly baseUrl;
17
+ constructor({ baseUrl }: RegistryClientOptions);
18
+ providers(options?: {
19
+ authToken?: string;
20
+ }): Promise<RegistryProviderRecord[]>;
21
+ registerLocalProvider(provider: RegistryProviderRecord): Promise<RegistryProviderRecord>;
22
+ unregisterLocalProvider(baseUrl: string): Promise<RegistryProviderRecord>;
23
+ registerSharedProvider(provider: RegistryProviderRecord, options?: {
24
+ authToken?: string;
25
+ }): Promise<RegistryProviderRecord>;
26
+ unregisterSharedProvider(baseUrl: string, options?: {
27
+ authToken?: string;
28
+ }): Promise<RegistryProviderRecord>;
29
+ providerAccessToken(baseUrl: string, options?: {
30
+ authToken?: string;
31
+ }): Promise<ProviderAccessTokenResource>;
32
+ startDockerWorker(options?: Partial<RegistryProviderRecord> & {
33
+ terminalThemeProfile?: TerminalThemeProfile;
34
+ }): Promise<RegistryProviderRecord>;
35
+ startLocalProvider(options: {
36
+ kind: "iterm" | "virtual-tmux" | "wezterm";
37
+ label?: string;
38
+ }): Promise<RegistryProviderRecord>;
39
+ signIn(identifier: string, password: string): Promise<AuthSignInResponse>;
40
+ session(authToken?: string): Promise<AuthSessionResource>;
41
+ signOut(authToken?: string): Promise<unknown>;
42
+ switchOrganization(organizationId: AuthOrganizationId, options?: {
43
+ authToken?: string;
44
+ }): Promise<AuthSessionResource>;
45
+ consoleLayout(options?: {
46
+ authToken?: string;
47
+ }): Promise<ConsoleLayout>;
48
+ saveConsoleLayoutPatch(patch: ConsoleLayout, options?: {
49
+ authToken?: string;
50
+ }): Promise<ConsoleLayout>;
51
+ orgCanvasLayout(options: {
52
+ authToken?: string;
53
+ canvasId: string;
54
+ organizationId: AuthOrganizationId;
55
+ }): Promise<ConsoleLayout>;
56
+ saveOrgCanvasLayoutPatch(patch: ConsoleLayout, options: {
57
+ authToken?: string;
58
+ canvasId: string;
59
+ organizationId: AuthOrganizationId;
60
+ }): Promise<ConsoleLayout>;
61
+ canvasComments(options: {
62
+ authToken?: string;
63
+ canvasId: string;
64
+ organizationId: AuthOrganizationId;
65
+ }): Promise<CanvasComment[]>;
66
+ createCanvasComment(options: {
67
+ authToken?: string;
68
+ body: string;
69
+ canvasId: string;
70
+ organizationId: AuthOrganizationId;
71
+ position: CanvasPoint;
72
+ }): Promise<CanvasComment>;
73
+ createCanvasCommentReply(options: {
74
+ authToken?: string;
75
+ body: string;
76
+ canvasId: string;
77
+ commentId: string;
78
+ organizationId: AuthOrganizationId;
79
+ }): Promise<CanvasComment>;
80
+ updateCanvasCommentResolution(options: {
81
+ authToken?: string;
82
+ canvasId: string;
83
+ commentId: string;
84
+ organizationId: AuthOrganizationId;
85
+ resolved: boolean;
86
+ }): Promise<CanvasComment>;
87
+ private orgCanvasPath;
88
+ private writeJson;
89
+ private requestJson;
90
+ private request;
91
+ private url;
92
+ }
93
+ export declare function registryClientForOrigin(baseUrl: string): RegistryClient;
@@ -0,0 +1,170 @@
1
+ import { asError } from "@termfleet/core/lib/errors.js";
2
+ import { parseCanvasComment, parseCanvasCommentsList } from "./contracts/canvas.js";
3
+ import { parseConsoleLayout } from "./contracts/console-layout.js";
4
+ import { normalizeRegistryPayload } from "./contracts/registry.js";
5
+ export class RegistryClient {
6
+ baseUrl;
7
+ constructor({ baseUrl }) {
8
+ this.baseUrl = normalizeBaseUrl(baseUrl);
9
+ }
10
+ async providers(options = {}) {
11
+ const response = await this.request("/api/registry/providers", {
12
+ headers: bearerHeaders(options.authToken)
13
+ });
14
+ return normalizeRegistryPayload(await readRegistryJson(response));
15
+ }
16
+ async registerLocalProvider(provider) {
17
+ return normalizeRegistryRecord(await this.writeJson("POST", "/api/registry/local-providers", provider));
18
+ }
19
+ async unregisterLocalProvider(baseUrl) {
20
+ const url = this.url("/api/registry/local-providers");
21
+ url.searchParams.set("baseUrl", baseUrl);
22
+ return normalizeRegistryRecord(await this.requestJson(url, { method: "DELETE" }));
23
+ }
24
+ async registerSharedProvider(provider, options = {}) {
25
+ return normalizeRegistryRecord(await this.writeJson("POST", "/api/registry/remote-providers", provider, bearerHeaders(options.authToken)));
26
+ }
27
+ async unregisterSharedProvider(baseUrl, options = {}) {
28
+ const url = this.url("/api/registry/remote-providers");
29
+ url.searchParams.set("baseUrl", baseUrl);
30
+ return normalizeRegistryRecord(await this.requestJson(url, {
31
+ headers: bearerHeaders(options.authToken),
32
+ method: "DELETE"
33
+ }));
34
+ }
35
+ async providerAccessToken(baseUrl, options = {}) {
36
+ return await this.writeJson("POST", "/api/registry/provider-tokens", { baseUrl }, bearerHeaders(options.authToken));
37
+ }
38
+ async startDockerWorker(options = {}) {
39
+ return normalizeRegistryRecord(await this.writeJson("POST", "/api/docker-worker/start", options));
40
+ }
41
+ async startLocalProvider(options) {
42
+ return normalizeRegistryRecord(await this.writeJson("POST", "/api/local-provider/start", options));
43
+ }
44
+ async signIn(identifier, password) {
45
+ return await this.writeJson("POST", "/api/auth/sign-in", { identifier, password });
46
+ }
47
+ async session(authToken) {
48
+ return await this.requestJson("/api/auth/session", {
49
+ headers: bearerHeaders(authToken)
50
+ });
51
+ }
52
+ async signOut(authToken) {
53
+ return await this.writeJson("POST", "/api/auth/sign-out", undefined, bearerHeaders(authToken));
54
+ }
55
+ async switchOrganization(organizationId, options = {}) {
56
+ return await this.writeJson("POST", "/api/auth/active-organization", { organizationId }, bearerHeaders(options.authToken));
57
+ }
58
+ async consoleLayout(options = {}) {
59
+ return parseConsoleLayout(await this.requestJson("/api/console-layout", {
60
+ headers: bearerHeaders(options.authToken)
61
+ }));
62
+ }
63
+ async saveConsoleLayoutPatch(patch, options = {}) {
64
+ return parseConsoleLayout(await this.writeJson("PATCH", "/api/console-layout", patch, bearerHeaders(options.authToken)));
65
+ }
66
+ async orgCanvasLayout(options) {
67
+ return parseConsoleLayout(await this.requestJson(this.orgCanvasPath(options.organizationId, options.canvasId, "layout"), {
68
+ headers: bearerHeaders(options.authToken)
69
+ }));
70
+ }
71
+ async saveOrgCanvasLayoutPatch(patch, options) {
72
+ return parseConsoleLayout(await this.writeJson("PATCH", this.orgCanvasPath(options.organizationId, options.canvasId, "layout"), patch, bearerHeaders(options.authToken)));
73
+ }
74
+ async canvasComments(options) {
75
+ return parseCanvasCommentsList(await this.requestJson(this.orgCanvasPath(options.organizationId, options.canvasId, "comments"), {
76
+ headers: bearerHeaders(options.authToken)
77
+ }));
78
+ }
79
+ async createCanvasComment(options) {
80
+ return parseCanvasComment(await this.writeJson("POST", this.orgCanvasPath(options.organizationId, options.canvasId, "comments"), {
81
+ body: options.body,
82
+ coordinateSpace: "canvas",
83
+ position: options.position
84
+ }, bearerHeaders(options.authToken)));
85
+ }
86
+ async createCanvasCommentReply(options) {
87
+ return parseCanvasComment(await this.writeJson("POST", `${this.orgCanvasPath(options.organizationId, options.canvasId, "comments")}/${encodeURIComponent(options.commentId)}/replies`, {
88
+ body: options.body
89
+ }, bearerHeaders(options.authToken)));
90
+ }
91
+ async updateCanvasCommentResolution(options) {
92
+ return parseCanvasComment(await this.writeJson("PATCH", `${this.orgCanvasPath(options.organizationId, options.canvasId, "comments")}/${encodeURIComponent(options.commentId)}`, {
93
+ resolved: options.resolved
94
+ }, bearerHeaders(options.authToken)));
95
+ }
96
+ orgCanvasPath(organizationId, canvasId, suffix) {
97
+ return `/api/registry/orgs/${encodeURIComponent(organizationId)}/canvases/${encodeURIComponent(canvasId)}/${suffix}`;
98
+ }
99
+ async writeJson(method, path, body, headers = {}) {
100
+ return await this.requestJson(path, {
101
+ body: body === undefined ? undefined : JSON.stringify(body),
102
+ headers: {
103
+ ...headers,
104
+ ...(body === undefined ? {} : { "content-type": "application/json" })
105
+ },
106
+ method
107
+ });
108
+ }
109
+ async requestJson(input, init) {
110
+ const response = await this.request(input, init);
111
+ return await readRegistryJson(response);
112
+ }
113
+ async request(input, init) {
114
+ const response = await fetch(input instanceof URL ? input : this.url(input), {
115
+ ...init,
116
+ credentials: init?.credentials ?? "include"
117
+ });
118
+ if (!response.ok) {
119
+ throw new Error(await readRegistryErrorMessage(response));
120
+ }
121
+ return response;
122
+ }
123
+ url(path) {
124
+ return new URL(path, this.baseUrl);
125
+ }
126
+ }
127
+ export function registryClientForOrigin(baseUrl) {
128
+ return new RegistryClient({ baseUrl });
129
+ }
130
+ function bearerHeaders(authToken) {
131
+ return authToken ? { authorization: `Bearer ${authToken}` } : {};
132
+ }
133
+ function normalizeRegistryRecord(value) {
134
+ const [record] = normalizeRegistryPayload([value]);
135
+ if (!record) {
136
+ throw new Error("Registry did not return the registered provider.");
137
+ }
138
+ return record;
139
+ }
140
+ function normalizeBaseUrl(baseUrl) {
141
+ return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
142
+ }
143
+ // Registry error responses are JSON like {"error":"..."} — surface the inner
144
+ // message rather than the raw body, so the UI never shows literal JSON.
145
+ async function readRegistryErrorMessage(response) {
146
+ const body = await response.text();
147
+ try {
148
+ const parsed = JSON.parse(body);
149
+ const message = typeof parsed.error === "string" ? parsed.error
150
+ : typeof parsed.message === "string" ? parsed.message
151
+ : undefined;
152
+ if (message)
153
+ return message;
154
+ }
155
+ catch {
156
+ // Not JSON — fall through to the raw text.
157
+ }
158
+ return body || `Registry request failed (${response.status}).`;
159
+ }
160
+ async function readRegistryJson(response) {
161
+ const body = await response.text();
162
+ try {
163
+ return JSON.parse(body);
164
+ }
165
+ catch (error) {
166
+ const contentType = response.headers.get("content-type") ?? "unknown content-type";
167
+ const preview = body.replace(/\s+/g, " ").slice(0, 120);
168
+ throw new Error(`Registry returned non-JSON from ${response.url} (${response.status}, ${contentType}): ${preview || asError(error).message}`);
169
+ }
170
+ }
@@ -0,0 +1,56 @@
1
+ import { type RegistryProviderRecord, type RegistryProviderSource } from "./contracts/registry.js";
2
+ export type ProviderRegistryOptions = {
3
+ localRegistryFile: string;
4
+ remoteRegistryUrl?: string;
5
+ remoteRegistryUrls?: string[];
6
+ remoteCacheTtlMs?: number;
7
+ onRemoteReadError?: (message: string) => void;
8
+ };
9
+ export declare class ProviderRegistry {
10
+ private readonly localRegistryFile;
11
+ private readonly remoteRegistryUrls;
12
+ private readonly remoteCacheTtlMs;
13
+ private readonly onRemoteReadError?;
14
+ private remoteRegistryCache;
15
+ private remoteRegistryRead?;
16
+ private readonly remoteTokenCache;
17
+ private readonly remoteTokenRead;
18
+ private lastRegistryReadFailure;
19
+ private localWriteChain;
20
+ constructor(options: ProviderRegistryOptions);
21
+ list(options?: {
22
+ authToken?: string;
23
+ runtimeProviders?: RegistryProviderRecord[];
24
+ }): Promise<RegistryProviderRecord[]>;
25
+ listRemoteWithToken(authToken: string | undefined): Promise<RegistryProviderRecord[]>;
26
+ resolveProvider(target: string, options?: {
27
+ authToken?: string;
28
+ runtimeProviders?: RegistryProviderRecord[];
29
+ }): Promise<RegistryProviderRecord>;
30
+ register(provider: RegistryProviderRecord): Promise<RegistryProviderRecord>;
31
+ registerLocal(provider: RegistryProviderRecord): Promise<RegistryProviderRecord>;
32
+ registerRemote(provider: RegistryProviderRecord, options?: {
33
+ authToken?: string;
34
+ }): Promise<RegistryProviderRecord>;
35
+ removeRemote(baseUrl: string, options?: {
36
+ authToken?: string;
37
+ }): Promise<RegistryProviderRecord>;
38
+ removeLocal(baseUrl: string): Promise<RegistryProviderRecord>;
39
+ readLocal(): Promise<RegistryProviderRecord[]>;
40
+ writeLocal(provider: RegistryProviderRecord): Promise<RegistryProviderRecord>;
41
+ stampInstanceId(baseUrl: string, instanceId: string): Promise<{
42
+ conflictWith?: string;
43
+ stamped: boolean;
44
+ }>;
45
+ private withLocalLock;
46
+ private writeLocalFile;
47
+ private readRemote;
48
+ private readRemoteForList;
49
+ private assertRemoteConfigured;
50
+ private fetchRemote;
51
+ private writeRemote;
52
+ private deleteRemote;
53
+ }
54
+ export declare function decorateRegistryProviders(providers: RegistryProviderRecord[], registrySource: RegistryProviderSource): RegistryProviderRecord[];
55
+ export declare function providerId(provider: RegistryProviderRecord): string;
56
+ export declare function normalizeProviderTarget(target: string): string;