@gitpod/gitpod-protocol 0.1.5-me-ingress.20 → 0.1.5-me-image.2

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 (105) hide show
  1. package/data/gitpod-schema.json +12 -0
  2. package/lib/admin-protocol.d.ts +10 -0
  3. package/lib/admin-protocol.d.ts.map +1 -1
  4. package/lib/admin-protocol.js.map +1 -1
  5. package/lib/analytics.d.ts +4 -5
  6. package/lib/analytics.d.ts.map +1 -1
  7. package/lib/context-url.d.ts +18 -5
  8. package/lib/context-url.d.ts.map +1 -1
  9. package/lib/context-url.js +58 -5
  10. package/lib/context-url.js.map +1 -1
  11. package/lib/context-url.spec.d.ts +2 -0
  12. package/lib/context-url.spec.d.ts.map +1 -1
  13. package/lib/context-url.spec.js +25 -4
  14. package/lib/context-url.spec.js.map +1 -1
  15. package/lib/gitpod-service.d.ts +23 -2
  16. package/lib/gitpod-service.d.ts.map +1 -1
  17. package/lib/gitpod-service.js +4 -2
  18. package/lib/gitpod-service.js.map +1 -1
  19. package/lib/headless-workspace-log.d.ts +17 -0
  20. package/lib/headless-workspace-log.d.ts.map +1 -1
  21. package/lib/headless-workspace-log.js +20 -1
  22. package/lib/headless-workspace-log.js.map +1 -1
  23. package/lib/ide-protocol.d.ts +24 -0
  24. package/lib/ide-protocol.d.ts.map +1 -1
  25. package/lib/index.d.ts +1 -0
  26. package/lib/index.d.ts.map +1 -1
  27. package/lib/index.js +1 -0
  28. package/lib/index.js.map +1 -1
  29. package/lib/installation-admin-protocol.d.ts +27 -0
  30. package/lib/installation-admin-protocol.d.ts.map +1 -0
  31. package/lib/installation-admin-protocol.js +30 -0
  32. package/lib/installation-admin-protocol.js.map +1 -0
  33. package/lib/messaging/error.d.ts +1 -0
  34. package/lib/messaging/error.d.ts.map +1 -1
  35. package/lib/messaging/error.js +2 -0
  36. package/lib/messaging/error.js.map +1 -1
  37. package/lib/messaging/node/connection.d.ts +1 -7
  38. package/lib/messaging/node/connection.d.ts.map +1 -1
  39. package/lib/messaging/node/connection.js +20 -18
  40. package/lib/messaging/node/connection.js.map +1 -1
  41. package/lib/permission.d.ts +7 -1
  42. package/lib/permission.d.ts.map +1 -1
  43. package/lib/permission.js +24 -4
  44. package/lib/permission.js.map +1 -1
  45. package/lib/protocol.d.ts +32 -38
  46. package/lib/protocol.d.ts.map +1 -1
  47. package/lib/protocol.js +16 -4
  48. package/lib/protocol.js.map +1 -1
  49. package/lib/teams-projects-protocol.d.ts +3 -0
  50. package/lib/teams-projects-protocol.d.ts.map +1 -1
  51. package/lib/teams-projects-protocol.js +7 -0
  52. package/lib/teams-projects-protocol.js.map +1 -1
  53. package/lib/util/generate-workspace-id.d.ts.map +1 -1
  54. package/lib/util/generate-workspace-id.js +1 -0
  55. package/lib/util/generate-workspace-id.js.map +1 -1
  56. package/lib/util/generate-workspace-id.spec.js +1 -0
  57. package/lib/util/generate-workspace-id.spec.js.map +1 -1
  58. package/lib/util/gitpod-host-url.d.ts +0 -1
  59. package/lib/util/gitpod-host-url.d.ts.map +1 -1
  60. package/lib/util/gitpod-host-url.js +0 -3
  61. package/lib/util/gitpod-host-url.js.map +1 -1
  62. package/lib/util/tracing.d.ts +2 -1
  63. package/lib/util/tracing.d.ts.map +1 -1
  64. package/lib/util/tracing.js +29 -6
  65. package/lib/util/tracing.js.map +1 -1
  66. package/lib/workspace-cluster.d.ts +9 -7
  67. package/lib/workspace-cluster.d.ts.map +1 -1
  68. package/lib/workspace-cluster.js +18 -1
  69. package/lib/workspace-cluster.js.map +1 -1
  70. package/lib/workspace-instance.d.ts +19 -0
  71. package/lib/workspace-instance.d.ts.map +1 -1
  72. package/package.json +2 -2
  73. package/pkg-yarn.lock +2 -2
  74. package/provenance-bundle.jsonl +1 -1
  75. package/src/admin-protocol.ts +17 -4
  76. package/src/analytics.ts +4 -6
  77. package/src/context-url.spec.ts +18 -4
  78. package/src/context-url.ts +62 -6
  79. package/src/gitpod-service.ts +33 -6
  80. package/src/headless-workspace-log.ts +24 -0
  81. package/src/ide-frontend-service.ts +1 -1
  82. package/src/ide-protocol.ts +27 -0
  83. package/src/index.ts +1 -0
  84. package/src/installation-admin-protocol.ts +42 -0
  85. package/src/messaging/error.ts +3 -0
  86. package/src/messaging/node/connection.ts +20 -23
  87. package/src/permission.ts +22 -3
  88. package/src/protocol.ts +48 -42
  89. package/src/teams-projects-protocol.ts +7 -1
  90. package/src/util/generate-workspace-id.spec.ts +1 -0
  91. package/src/util/generate-workspace-id.ts +1 -0
  92. package/src/util/gitpod-host-url.ts +1 -5
  93. package/src/util/tracing.ts +29 -7
  94. package/src/workspace-cluster.ts +17 -9
  95. package/src/workspace-instance.ts +20 -0
  96. package/lib/messaging/connection-error-handler.d.ts +0 -27
  97. package/lib/messaging/connection-error-handler.d.ts.map +0 -1
  98. package/lib/messaging/connection-error-handler.js +0 -34
  99. package/lib/messaging/connection-error-handler.js.map +0 -1
  100. package/lib/util/safe-promise.d.ts +0 -11
  101. package/lib/util/safe-promise.d.ts.map +0 -1
  102. package/lib/util/safe-promise.js +0 -31
  103. package/lib/util/safe-promise.js.map +0 -1
  104. package/src/messaging/connection-error-handler.ts +0 -62
  105. package/src/util/safe-promise.ts +0 -26
@@ -5,9 +5,13 @@
5
5
  */
6
6
 
7
7
  import { User, Workspace, NamedWorkspaceFeatureFlag } from "./protocol";
8
+ import { FindPrebuildsParams } from "./gitpod-service";
9
+ import { PrebuildWithStatus } from "./teams-projects-protocol"
10
+ import { Project, Team } from "./teams-projects-protocol";
8
11
  import { WorkspaceInstance, WorkspaceInstancePhase } from "./workspace-instance";
9
12
  import { RoleOrPermission } from "./permission";
10
13
  import { AccountStatement } from "./accounting-protocol";
14
+ import { InstallationAdminSettings } from "./installation-admin-protocol";
11
15
 
12
16
  export interface AdminServer {
13
17
  adminGetUsers(req: AdminGetListRequest<User>): Promise<AdminGetListResult<User>>;
@@ -17,11 +21,17 @@ export interface AdminServer {
17
21
  adminModifyRoleOrPermission(req: AdminModifyRoleOrPermissionRequest): Promise<User>;
18
22
  adminModifyPermanentWorkspaceFeatureFlag(req: AdminModifyPermanentWorkspaceFeatureFlagRequest): Promise<User>;
19
23
 
24
+ adminGetTeamById(id: string): Promise<Team | undefined>;
25
+
20
26
  adminGetWorkspaces(req: AdminGetWorkspacesRequest): Promise<AdminGetListResult<WorkspaceAndInstance>>;
21
27
  adminGetWorkspace(id: string): Promise<WorkspaceAndInstance>;
22
28
  adminForceStopWorkspace(id: string): Promise<void>;
23
29
  adminRestoreSoftDeletedWorkspace(id: string): Promise<void>;
24
30
 
31
+ adminGetProjectsBySearchTerm(req: AdminGetListRequest<Project>): Promise<AdminGetListResult<Project>>;
32
+ adminGetProjectById(id: string): Promise<Project | undefined>;
33
+
34
+ adminFindPrebuilds(params: FindPrebuildsParams): Promise<PrebuildWithStatus[]>;
25
35
  adminSetLicense(key: string): Promise<void>;
26
36
 
27
37
  adminGetAccountStatement(userId: string): Promise<AccountStatement>;
@@ -29,6 +39,9 @@ export interface AdminServer {
29
39
  adminIsStudent(userId: string): Promise<boolean>;
30
40
  adminAddStudentEmailDomain(userId: string, domain: string): Promise<void>;
31
41
  adminGrantExtraHours(userId: string, extraHours: number): Promise<void>;
42
+
43
+ adminGetSettings(): Promise<InstallationAdminSettings>
44
+ adminUpdateSettings(settings: InstallationAdminSettings): Promise<void>
32
45
  }
33
46
 
34
47
  export interface AdminGetListRequest<T> {
@@ -65,7 +78,7 @@ export interface AdminModifyPermanentWorkspaceFeatureFlagRequest {
65
78
  }[]
66
79
  }
67
80
 
68
- export interface WorkspaceAndInstance extends Omit<Workspace, "id"|"creationTime">, Omit<WorkspaceInstance, "id"|"creationTime"> {
81
+ export interface WorkspaceAndInstance extends Omit<Workspace, "id" | "creationTime">, Omit<WorkspaceInstance, "id" | "creationTime"> {
69
82
  workspaceId: string;
70
83
  workspaceCreationTime: string;
71
84
  instanceId: string;
@@ -78,7 +91,7 @@ export namespace WorkspaceAndInstance {
78
91
  return {
79
92
  id: wai.workspaceId,
80
93
  creationTime: wai.workspaceCreationTime,
81
- ... wai
94
+ ...wai
82
95
  };
83
96
  }
84
97
 
@@ -89,7 +102,7 @@ export namespace WorkspaceAndInstance {
89
102
  return {
90
103
  id: wai.instanceId,
91
104
  creationTime: wai.instanceCreationTime,
92
- ... wai
105
+ ...wai
93
106
  };
94
107
  }
95
108
  }
@@ -102,4 +115,4 @@ export type AdminGetWorkspacesQuery = {
102
115
  instanceId?: string;
103
116
  workspaceId?: string;
104
117
  ownerId?: string;
105
- };
118
+ };
package/src/analytics.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  export const IAnalyticsWriter = Symbol("IAnalyticsWriter");
9
9
 
10
10
  type Identity =
11
- | { userId: string | number }
11
+ | { userId: string | number; anonymousId?: string | number }
12
12
  | { userId?: string | number; anonymousId: string | number };
13
13
 
14
14
  interface Message {
@@ -34,14 +34,12 @@ export type PageMessage = Message & Identity & {
34
34
  context?: any;
35
35
  };
36
36
 
37
- export type RemoteTrackMessage = Omit<TrackMessage, "timestamp" | "userId" | "anonymousId"> & {
38
- anonymousId?: string | number;
39
- };
37
+ export type RemoteTrackMessage = Omit<TrackMessage, "timestamp" | "userId">;
40
38
  export type RemotePageMessage = Omit<PageMessage, "timestamp" | "userId"> & {
41
- anonymousId?: string | number;
39
+ includePII?: boolean;
42
40
  };
43
41
 
44
- export type RemoteIdentifyMessage = Omit<IdentifyMessage, "timestamp" | "userId" | "anonymousId">;
42
+ export type RemoteIdentifyMessage = Omit<IdentifyMessage, "timestamp" | "userId">;
45
43
 
46
44
  export interface IAnalyticsWriter {
47
45
 
@@ -6,34 +6,48 @@
6
6
 
7
7
  import * as chai from 'chai';
8
8
  import { suite, test } from 'mocha-typescript';
9
+ import { Workspace } from '.';
9
10
  import { ContextURL } from './context-url';
10
11
  const expect = chai.expect;
11
12
 
13
+ type WsContextUrl = Pick<Workspace, "context" | "contextURL">;
14
+
12
15
  @suite
13
16
  export class ContextUrlTest {
14
17
 
15
18
  @test public parseContextUrl_withEnvVar() {
16
- const actual = ContextURL.parseToURL("passedin=test%20value/https://github.com/gitpod-io/gitpod-test-repo");
19
+ const actual = ContextURL.getNormalizedURL({ contextURL: "passedin=test%20value/https://github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl);
17
20
  expect(actual?.host).to.equal("github.com");
18
21
  expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo");
19
22
  }
20
23
 
21
24
  @test public parseContextUrl_withEnvVar_withoutSchema() {
22
- const actual = ContextURL.parseToURL("passedin=test%20value/github.com/gitpod-io/gitpod-test-repo");
25
+ const actual = ContextURL.getNormalizedURL({ contextURL: "passedin=test%20value/github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl);
23
26
  expect(actual?.host).to.equal("github.com");
24
27
  expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo");
25
28
  }
26
29
 
30
+ @test public parseContextUrl_withEnvVar_sshUrl() {
31
+ const actual = ContextURL.getNormalizedURL({ contextURL: "passedin=test%20value/git@github.com:gitpod-io/gitpod-test-repo.git", context: {} } as WsContextUrl);
32
+ expect(actual?.host).to.equal("github.com");
33
+ expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo.git");
34
+ }
35
+
27
36
  @test public parseContextUrl_withPrebuild() {
28
- const actual = ContextURL.parseToURL("prebuild/https://github.com/gitpod-io/gitpod-test-repo");
37
+ const actual = ContextURL.getNormalizedURL({ contextURL: "prebuild/https://github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl);
29
38
  expect(actual?.host).to.equal("github.com");
30
39
  expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo");
31
40
  }
32
41
 
33
42
  @test public parseContextUrl_withPrebuild_withoutSchema() {
34
- const actual = ContextURL.parseToURL("prebuild/github.com/gitpod-io/gitpod-test-repo");
43
+ const actual = ContextURL.getNormalizedURL({ contextURL: "prebuild/github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl);
35
44
  expect(actual?.host).to.equal("github.com");
36
45
  expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo");
37
46
  }
47
+
48
+ @test public parseContextUrl_badUrl() {
49
+ const actual = ContextURL.getNormalizedURL({ contextURL: "[Object object]", context: {} } as WsContextUrl);
50
+ expect(actual).to.be.undefined;
51
+ }
38
52
  }
39
53
  module.exports = new ContextUrlTest()
@@ -4,10 +4,62 @@
4
4
  * See License-AGPL.txt in the project root for license information.
5
5
  */
6
6
 
7
+ import { Workspace } from ".";
8
+
9
+ /**
10
+ * The whole point of these methods is to overcome inconsistencies in our data model.
11
+ * Ideally we remove it at some point once we fixed our model, as it:
12
+ * - duplicates logic
13
+ * - but additional burden on clients (using this, copying this to other languages!)
14
+ *
15
+ * TODO(gpl) See if we can get this into `server` code to remove the burden from clients
16
+ */
7
17
  export namespace ContextURL {
8
18
  export const INCREMENTAL_PREBUILD_PREFIX = "incremental-prebuild";
9
19
  export const PREBUILD_PREFIX = "prebuild";
10
20
  export const IMAGEBUILD_PREFIX = "imagebuild";
21
+ export const SNAPSHOT_PREFIX = "snapshot";
22
+ export const REFERRER_PREFIX = 'referrer:';
23
+
24
+ /**
25
+ * This function will (try to) return the HTTP(S) URL of the context the user originally created this workspace on.
26
+ * Especially it will not contain any modifiers or be of different scheme than HTTP(S).
27
+ *
28
+ * Use this function if you need to provided a _working_ URL to the original context.
29
+ * @param ws
30
+ * @returns
31
+ */
32
+ export function getNormalizedURL(ws: Pick<Workspace, "context" | "contextURL"> | undefined): URL | undefined {
33
+ const normalized = normalize(ws);
34
+ if (!normalized) {
35
+ return undefined;
36
+ }
37
+
38
+ try {
39
+ return new URL(normalized);
40
+ } catch (err) {
41
+ console.error(`unable to parse URL from normalized contextURL: '${normalized}'`, err);
42
+ }
43
+ return undefined;
44
+ }
45
+
46
+ function normalize(ws: Pick<Workspace, "context" | "contextURL"> | undefined): string | undefined {
47
+ if (!ws) {
48
+ return undefined;
49
+ }
50
+ if (ws.context.normalizedContextURL) {
51
+ return ws.context.normalizedContextURL;
52
+ }
53
+
54
+ // fallback: we do not yet set normalizedContextURL on all workspaces, yet, let alone older existing workspaces
55
+ let fallback: string | undefined = undefined;
56
+ try {
57
+ fallback = removePrefixes(ws.contextURL);
58
+ } catch (err) {
59
+ console.error(`unable to remove prefixes from contextURL: '${ws.contextURL}'`, err);
60
+ }
61
+ return fallback;
62
+ }
11
63
 
12
64
  /**
13
65
  * The field "contextUrl" might contain prefixes like:
@@ -15,29 +67,33 @@ export namespace ContextURL {
15
67
  * - prebuild/...
16
68
  * This is the analogon to the (Prefix)ContextParser structure in "server".
17
69
  */
18
- export function parseToURL(contextUrl: string | undefined): URL | undefined {
70
+ function removePrefixes(contextUrl: string | undefined): string | undefined {
19
71
  if (contextUrl === undefined) {
20
72
  return undefined;
21
73
  }
22
74
 
23
75
  const segments = contextUrl.split("/");
24
76
  if (segments.length === 1) {
25
- return new URL(segments[0]); // this might be something, we just try
77
+ return segments[0]; // this might be something, we just try
26
78
  }
27
79
 
28
- const segmentsToURL = (offset: number): URL => {
80
+ const segmentsToURL = (offset: number): string => {
29
81
  let rest = segments.slice(offset).join("/");
82
+ if (/^git@[^:\/]+:/.test(rest)) {
83
+ rest = rest.replace(/^git@([^:\/]+):/, 'https://$1/');
84
+ }
30
85
  if (!rest.startsWith("http")) {
31
86
  rest = 'https://' + rest;
32
87
  }
33
- return new URL(rest);
88
+ return rest;
34
89
  };
35
90
 
36
-
37
91
  const firstSegment = segments[0];
38
92
  if (firstSegment === PREBUILD_PREFIX ||
39
93
  firstSegment === INCREMENTAL_PREBUILD_PREFIX ||
40
- firstSegment === IMAGEBUILD_PREFIX) {
94
+ firstSegment === IMAGEBUILD_PREFIX ||
95
+ firstSegment === SNAPSHOT_PREFIX ||
96
+ firstSegment.startsWith(REFERRER_PREFIX)) {
41
97
  return segmentsToURL(1);
42
98
  }
43
99
 
@@ -6,10 +6,10 @@
6
6
 
7
7
  import {
8
8
  User, WorkspaceInfo, WorkspaceCreationResult, WorkspaceInstanceUser,
9
- WhitelistedRepository, WorkspaceImageBuild, AuthProviderInfo, Branding, CreateWorkspaceMode,
9
+ WhitelistedRepository, WorkspaceImageBuild, AuthProviderInfo, CreateWorkspaceMode,
10
10
  Token, UserEnvVarValue, ResolvePluginsParams, PreparePluginUploadParams, Terms,
11
11
  ResolvedPlugins, Configuration, InstallPluginsParams, UninstallPluginParams, UserInfo, GitpodTokenType,
12
- GitpodToken, AuthProviderEntry, GuessGitTokenScopesParams, GuessedGitTokenScopes
12
+ GitpodToken, AuthProviderEntry, GuessGitTokenScopesParams, GuessedGitTokenScopes, ProjectEnvVar
13
13
  } from './protocol';
14
14
  import {
15
15
  Team, TeamMemberInfo,
@@ -30,6 +30,7 @@ import { GithubUpgradeURL, PlanCoupon } from './payment-protocol';
30
30
  import { TeamSubscription, TeamSubscriptionSlot, TeamSubscriptionSlotResolved } from './team-subscription-protocol';
31
31
  import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from './analytics';
32
32
  import { IDEServer } from './ide-protocol';
33
+ import { InstallationAdminSettings, TelemetryData } from './installation-admin-protocol';
33
34
 
34
35
  export interface GitpodClient {
35
36
  onInstanceUpdate(instance: WorkspaceInstance): void;
@@ -55,7 +56,6 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
55
56
  getOwnAuthProviders(): Promise<AuthProviderEntry[]>;
56
57
  updateOwnAuthProvider(params: GitpodServer.UpdateOwnAuthProviderParams): Promise<AuthProviderEntry>;
57
58
  deleteOwnAuthProvider(params: GitpodServer.DeleteOwnAuthProviderParams): Promise<void>;
58
- getBranding(): Promise<Branding>;
59
59
  getConfiguration(): Promise<Configuration>;
60
60
  getToken(query: GitpodServer.GetTokenSearchOptions): Promise<Token | undefined>;
61
61
  getGitpodTokenScopes(tokenHash: string): Promise<string[]>;
@@ -72,8 +72,15 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
72
72
  getWorkspaceOwner(workspaceId: string): Promise<UserInfo | undefined>;
73
73
  getWorkspaceUsers(workspaceId: string): Promise<WorkspaceInstanceUser[]>;
74
74
  getFeaturedRepositories(): Promise<WhitelistedRepository[]>;
75
+ getSuggestedContextURLs(): Promise<string[]>;
76
+ /**
77
+ * **Security:**
78
+ * Sensitive information like an owner token is erased, since it allows access for all team members.
79
+ * If you need to access an owner token use `getOwnerToken` instead.
80
+ */
75
81
  getWorkspace(id: string): Promise<WorkspaceInfo>;
76
82
  isWorkspaceOwner(workspaceId: string): Promise<boolean>;
83
+ getOwnerToken(workspaceId: string): Promise<string>;
77
84
 
78
85
  /**
79
86
  * Creates and starts a workspace for the given context URL.
@@ -126,6 +133,11 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
126
133
  resetGenericInvite(inviteId: string): Promise<TeamMembershipInvite>;
127
134
  deleteTeam(teamId: string, userId: string): Promise<void>;
128
135
 
136
+ // Admin Settings
137
+ adminGetSettings(): Promise<InstallationAdminSettings>;
138
+ adminUpdateSettings(settings: InstallationAdminSettings): Promise<void>;
139
+ adminGetTelemetryData(): Promise<TelemetryData>;
140
+
129
141
  // Projects
130
142
  getProviderRepositoriesForUser(params: GetProviderRepositoriesParams): Promise<ProviderRepository[]>;
131
143
  createProject(params: CreateProjectParams): Promise<Project>;
@@ -142,6 +154,9 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
142
154
  guessRepositoryConfiguration(cloneUrl: string): Promise<string | undefined>;
143
155
  setProjectConfiguration(projectId: string, configString: string): Promise<void>;
144
156
  updateProjectPartial(partialProject: PartialProject): Promise<void>;
157
+ setProjectEnvironmentVariable(projectId: string, name: string, value: string, censored: boolean): Promise<void>;
158
+ getProjectEnvironmentVariables(projectId: string): Promise<ProjectEnvVar[]>;
159
+ deleteProjectEnvironmentVariable(variableId: string): Promise<void>;
145
160
 
146
161
  // content service
147
162
  getContentBlobUploadUrl(name: string): Promise<string>
@@ -239,6 +254,16 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
239
254
  identifyUser(event: RemoteIdentifyMessage): Promise<void>;
240
255
  }
241
256
 
257
+ export interface RateLimiterError {
258
+ method?: string,
259
+
260
+ /**
261
+ * Retry after this many seconds, earliest.
262
+ * cmp.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
263
+ */
264
+ retryAfter: number,
265
+ }
266
+
242
267
  export interface CreateProjectParams {
243
268
  name: string;
244
269
  slug?: string;
@@ -384,10 +409,12 @@ export class GitpodCompositeClient<Client extends GitpodClient> implements Gitpo
384
409
 
385
410
  public registerClient(client: Partial<Client>): Disposable {
386
411
  this.clients.push(client);
387
- const index = this.clients.length;
388
412
  return {
389
413
  dispose: () => {
390
- this.clients.slice(index, 1);
414
+ const index = this.clients.indexOf(client);
415
+ if (index > -1) {
416
+ this.clients.splice(index, 1);
417
+ }
391
418
  }
392
419
  }
393
420
  }
@@ -577,7 +604,7 @@ export class WorkspaceInstanceUpdateListener {
577
604
  }
578
605
 
579
606
  export interface GitpodServiceOptions {
580
- onReconnect?: () => (void | Promise<void>)
607
+ onReconnect?: () => (void | Promise<void>)
581
608
  }
582
609
 
583
610
  export class GitpodServiceImpl<Client extends GitpodClient, Server extends GitpodServer> {
@@ -4,6 +4,30 @@
4
4
  * See License-AGPL.txt in the project root for license information.
5
5
  */
6
6
 
7
+
8
+ export enum HeadlessWorkspaceEventType {
9
+ LogOutput = "log-output",
10
+ FinishedSuccessfully = "finish-success",
11
+ FinishedButFailed = "finish-fail",
12
+ AbortedTimedOut = "aborted-timeout",
13
+ Aborted = "aborted",
14
+ Started = "started"
15
+ }
16
+ export namespace HeadlessWorkspaceEventType {
17
+ export function isRunning(t: HeadlessWorkspaceEventType) {
18
+ return t === HeadlessWorkspaceEventType.LogOutput;
19
+ }
20
+ export function didFinish(t: HeadlessWorkspaceEventType) {
21
+ return t === HeadlessWorkspaceEventType.FinishedButFailed || t === HeadlessWorkspaceEventType.FinishedSuccessfully;
22
+ }
23
+ }
24
+
25
+ export interface HeadlessWorkspaceEvent {
26
+ workspaceID: string;
27
+ text: string;
28
+ type: HeadlessWorkspaceEventType;
29
+ }
30
+
7
31
  export interface HeadlessLogUrls {
8
32
  // A map of id to URL
9
33
  streams: { [streamID: string]: string };
@@ -7,7 +7,7 @@
7
7
  import { Event } from "./util/event";
8
8
  import { Disposable } from "./util/disposable";
9
9
 
10
- export type IDEFrontendState = 'init' | 'ready' | 'terminated';
10
+ export type IDEFrontendState = 'init' | 'ready' | 'terminated';
11
11
 
12
12
  export interface IDEFrontendService {
13
13
  readonly state: IDEFrontendState;
@@ -29,6 +29,28 @@ export interface IDEOptions {
29
29
  * The default desktop IDE when the user has not specified one.
30
30
  */
31
31
  defaultDesktopIde: string;
32
+
33
+ /**
34
+ * Client specific IDE options.
35
+ */
36
+ clients?: { [id: string]: IDEClient };
37
+ }
38
+
39
+ export interface IDEClient {
40
+ /**
41
+ * The default desktop IDE when the user has not specified one.
42
+ */
43
+ defaultDesktopIDE?: string;
44
+
45
+ /**
46
+ * Desktop IDEs supported by the client.
47
+ */
48
+ desktopIDEs?: string[]
49
+
50
+ /**
51
+ * Steps to install the client on user machine.
52
+ */
53
+ installationSteps?: string[]
32
54
  }
33
55
 
34
56
  export interface IDEOption {
@@ -80,6 +102,11 @@ export interface IDEOption {
80
102
  */
81
103
  image: string;
82
104
 
105
+ /**
106
+ * The latest image ref to the IDE image, this image ref always resolve to digest.
107
+ */
108
+ latestImage?: string;
109
+
83
110
  /**
84
111
  * When this is `true`, the tag of this image is resolved to the latest
85
112
  * image digest regularly.
package/src/index.ts CHANGED
@@ -19,3 +19,4 @@ export * from './context-url';
19
19
  export * from './teams-projects-protocol';
20
20
  export * from './snapshot-url';
21
21
  export * from './oss-allowlist';
22
+ export * from './installation-admin-protocol';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3
+ * Licensed under the GNU Affero General Public License (AGPL).
4
+ * See License-AGPL.txt in the project root for license information.
5
+ */
6
+
7
+ import { v4 as uuidv4 } from 'uuid';
8
+
9
+ const InstallationAdminSettingsPrototype = {
10
+ sendTelemetry: true
11
+ }
12
+
13
+ export type InstallationAdminSettings = typeof InstallationAdminSettingsPrototype;
14
+
15
+ export namespace InstallationAdminSettings {
16
+ export function fields(): (keyof InstallationAdminSettings)[] {
17
+ return Object.keys(InstallationAdminSettingsPrototype) as (keyof InstallationAdminSettings)[];
18
+ }
19
+ }
20
+
21
+ export interface InstallationAdmin {
22
+ id: string;
23
+ settings: InstallationAdminSettings;
24
+ }
25
+
26
+ export interface TelemetryData {
27
+ installationAdmin: InstallationAdmin
28
+ totalUsers: number
29
+ totalWorkspaces: number
30
+ totalInstances: number
31
+ }
32
+
33
+ export namespace InstallationAdmin {
34
+ export function createDefault(): InstallationAdmin {
35
+ return {
36
+ id: uuidv4(),
37
+ settings: {
38
+ ...InstallationAdminSettingsPrototype,
39
+ }
40
+ };
41
+ }
42
+ }
@@ -78,4 +78,7 @@ export namespace ErrorCodes {
78
78
 
79
79
  // 630 Snapshot Error
80
80
  export const SNAPSHOT_ERROR = 630;
81
+
82
+ // 640 Headless logs are not available (yet)
83
+ export const HEADLESS_LOG_NOT_YET_AVAILABLE = 640;
81
84
  }
@@ -6,37 +6,34 @@
6
6
  */
7
7
 
8
8
  import * as ws from "ws";
9
- import * as http from "http";
10
- import * as net from "net";
11
9
  import { IWebSocket } from "vscode-ws-jsonrpc";
12
- import { log } from '../../util/logging';
13
10
 
14
- export interface OnOpen {
15
- (webSocket: ws, request: http.IncomingMessage, socket: net.Socket, head: Buffer): void;
16
- }
17
-
18
- export function toIWebSocket(webSocket: ws) {
19
- let sendsAfterOpen = 0;
11
+ export function toIWebSocket(ws: ws) {
20
12
  return <IWebSocket>{
21
13
  send: content => {
22
- if (webSocket.readyState !== ws.OPEN) {
23
- if (sendsAfterOpen++ > 3) {
24
- //log.debug(`Repeated try to send on closed web socket (readyState was ${webSocket.readyState})`, { ws });
25
- }
14
+ if (ws.readyState >= ws.CLOSING) {
15
+ // ws is already CLOSING/CLOSED, send() would just return an error.
26
16
  return;
27
17
  }
28
- webSocket.send(content, err => {
29
- if (err) {
30
- log.error('error in ws.send()', err, { ws });
31
- }
32
- })
18
+
19
+ // in general send-errors should trigger an 'error' event already, we just make sure it actually happens.
20
+ try {
21
+ ws.send(content, err => {
22
+ if (!err) {
23
+ return;
24
+ }
25
+ ws.emit('error', err);
26
+ });
27
+ } catch (err) {
28
+ ws.emit('error', err);
29
+ }
33
30
  },
34
- onMessage: cb => webSocket.on('message', cb),
35
- onError: cb => webSocket.on('error', cb),
36
- onClose: cb => webSocket.on('close', cb),
31
+ onMessage: cb => ws.on('message', cb),
32
+ onError: cb => ws.on('error', cb),
33
+ onClose: cb => ws.on('close', cb),
37
34
  dispose: () => {
38
- if (webSocket.readyState < ws.CLOSING) {
39
- webSocket.close();
35
+ if (ws.readyState < ws.CLOSING) {
36
+ ws.close();
40
37
  }
41
38
  }
42
39
  };
package/src/permission.ts CHANGED
@@ -13,6 +13,7 @@ export const Permissions = {
13
13
  "registry-access": undefined,
14
14
  "admin-users": undefined,
15
15
  "admin-workspaces": undefined,
16
+ "admin-projects": undefined,
16
17
  "admin-api": undefined,
17
18
  "ide-settings": undefined,
18
19
  "new-workspace-cluster": undefined,
@@ -34,6 +35,22 @@ export interface Role {
34
35
  permissions: PermissionName[],
35
36
  }
36
37
 
38
+ export namespace RolesOrPermissions {
39
+ export function toPermissionSet(rolesOrPermissions: RoleOrPermission[] | undefined): Set<PermissionName> {
40
+ rolesOrPermissions = rolesOrPermissions || [];
41
+
42
+ const permissions = new Set<PermissionName>();
43
+ for (const rop of rolesOrPermissions) {
44
+ if (Permission.is(rop)) {
45
+ permissions.add(rop);
46
+ } else if (RoleName.is(rop)) {
47
+ Role.getByName(rop).permissions.forEach(p => permissions.add(p));
48
+ }
49
+ }
50
+ return permissions;
51
+ };
52
+ }
53
+
37
54
  export namespace Permission {
38
55
  /** The permission to monitor the (live) state of a Gitpod installation */
39
56
  export const MONITOR: PermissionName = "monitor";
@@ -50,6 +67,9 @@ export namespace Permission {
50
67
  /** The permission for accessing all workspace data */
51
68
  export const ADMIN_WORKSPACES: PermissionName = "admin-workspaces";
52
69
 
70
+ /** The permission for accessing all projects data */
71
+ export const ADMIN_PROJECTS: PermissionName = "admin-projects";
72
+
53
73
  /** The permission to access the admin API */
54
74
  export const ADMIN_API: PermissionName = "admin-api";
55
75
 
@@ -62,9 +82,7 @@ export namespace Permission {
62
82
  }
63
83
 
64
84
  export const all = (): PermissionName[] => {
65
- return Object.keys(Permission)
66
- .map(k => (Permission as any)[k])
67
- .filter(k => typeof(k) === 'string');
85
+ return Object.keys(Permissions) as PermissionName[];
68
86
  };
69
87
  }
70
88
 
@@ -94,6 +112,7 @@ export namespace Role {
94
112
  permissions: [
95
113
  Permission.ADMIN_USERS,
96
114
  Permission.ADMIN_WORKSPACES,
115
+ Permission.ADMIN_PROJECTS,
97
116
  Permission.ADMIN_API,
98
117
  Permission.ENFORCEMENT,
99
118
  ]