@codingame/monaco-vscode-chat-cloud-service-override 0.0.0-stage → 38.0.7
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/common.d.ts +2 -0
- package/common.js +29 -0
- package/index.d.ts +3 -0
- package/index.js +9 -0
- package/package.json +52 -3
- package/session.d.ts +3 -0
- package/session.js +11 -0
- package/vscode/src/vs/sessions/contrib/chat/browser/newSession.d.ts +9 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudAutomationStore.d.ts +67 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudAutomationStore.js +695 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudSandboxSessionConfig.d.ts +4 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudSandboxSessionConfig.js +39 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessions.contribution.d.ts +1 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessions.contribution.js +59 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsActions.d.ts +1 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsActions.js +83 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsChangesets.d.ts +87 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsChangesets.js +176 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsProvider.d.ts +422 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsProvider.js +2444 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/githubCloudAutomationStore.d.ts +91 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/githubCloudAutomationStore.js +620 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHost.contribution.d.ts +1 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHost.contribution.js +5 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostContribution.d.ts +46 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostContribution.js +321 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxSessionsProvider.d.ts +104 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxSessionsProvider.js +345 -0
- package/README.md +0 -3
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
|
|
2
|
+
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
|
|
3
|
+
import { Sequencer, Limiter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
4
|
+
import { CancellationToken } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
|
|
5
|
+
import { isCancellationError, CancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
6
|
+
import { Disposable, DisposableStore } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
7
|
+
import '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/index';
|
|
8
|
+
import { isObject } from '@codingame/monaco-vscode-api/vscode/vs/base/common/types';
|
|
9
|
+
import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
|
|
10
|
+
import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
|
|
11
|
+
import { IDefaultAccountService } from '@codingame/monaco-vscode-api/vscode/vs/platform/defaultAccount/common/defaultAccount.service';
|
|
12
|
+
import { toAbortSignal } from '@codingame/monaco-vscode-api/vscode/vs/platform/github/common/cancellation';
|
|
13
|
+
import { MutationUncertainError, ApiRequestError } from '@codingame/monaco-vscode-api/vscode/vs/platform/github/common/missionControl/missionControlClient';
|
|
14
|
+
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
|
|
15
|
+
import { StorageScope, StorageTarget } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage';
|
|
16
|
+
import { IStorageService } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage.service';
|
|
17
|
+
import { IWorkbenchGitHubService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/github/common/githubService.service';
|
|
18
|
+
import { ISessionsRecentWorkspacesService } from '@codingame/monaco-vscode-api/vscode/vs/sessions/services/sessions/browser/sessionsRecentWorkspacesService.service';
|
|
19
|
+
import { GITHUB_REMOTE_FILE_SCHEME } from '@codingame/monaco-vscode-api/vscode/vs/sessions/services/sessions/common/session';
|
|
20
|
+
import { observableValue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableValue';
|
|
21
|
+
import { constObservable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/constObservable';
|
|
22
|
+
import { transaction } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/transaction';
|
|
23
|
+
|
|
24
|
+
const REPOSITORIES_STORAGE_KEY = "cloudAutomations.repositories";
|
|
25
|
+
let GitHubCloudAutomationStore = class GitHubCloudAutomationStore extends Disposable {
|
|
26
|
+
constructor(
|
|
27
|
+
resolveRepositoryUri,
|
|
28
|
+
gitHubService,
|
|
29
|
+
defaultAccountService,
|
|
30
|
+
storageService,
|
|
31
|
+
logService,
|
|
32
|
+
recentWorkspacesService
|
|
33
|
+
) {
|
|
34
|
+
super();
|
|
35
|
+
this.resolveRepositoryUri = resolveRepositoryUri;
|
|
36
|
+
this.gitHubService = gitHubService;
|
|
37
|
+
this.defaultAccountService = defaultAccountService;
|
|
38
|
+
this.storageService = storageService;
|
|
39
|
+
this.logService = logService;
|
|
40
|
+
this.recentWorkspacesService = recentWorkspacesService;
|
|
41
|
+
this.cachedEntries = observableValue(this, []);
|
|
42
|
+
this.entries = this.cachedEntries;
|
|
43
|
+
this.state = observableValue(this, "ready");
|
|
44
|
+
this.catalogueState = this.state;
|
|
45
|
+
this.lifetime = ( new AbortController());
|
|
46
|
+
this.clientStore = this._register(( new DisposableStore()));
|
|
47
|
+
this.operations = ( new Sequencer());
|
|
48
|
+
this.cachedHistory = observableValue(this, []);
|
|
49
|
+
this.history = this.cachedHistory;
|
|
50
|
+
this.uncertain = observableValue(this, false);
|
|
51
|
+
this.mutationUncertain = this.uncertain;
|
|
52
|
+
this.targetEligibility = ( new Map());
|
|
53
|
+
this.targetEligibilityLimiter = this._register(( new Limiter(5)));
|
|
54
|
+
this.cachedTools = observableValue(this, undefined);
|
|
55
|
+
this.toolCatalog = this.cachedTools;
|
|
56
|
+
this._register(defaultAccountService.onDidChangeDefaultAccount(() => this.reset()));
|
|
57
|
+
this._register(gitHubService.onDidChangeDefaultClient(() => this.reset()));
|
|
58
|
+
this.reset();
|
|
59
|
+
}
|
|
60
|
+
getWorkspaceTarget(workspace) {
|
|
61
|
+
const required = ( localize(5091, "Choose a private GitHub.com repository."));
|
|
62
|
+
if (!workspace || this._store.isDisposed || this.lifetime.signal.aborted) {
|
|
63
|
+
return constObservable({
|
|
64
|
+
disabledReason: required
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const key = ( workspace.toString());
|
|
68
|
+
const cached = this.targetEligibility.get(key);
|
|
69
|
+
if (cached) {
|
|
70
|
+
return cached;
|
|
71
|
+
}
|
|
72
|
+
const result = observableValue(this, {
|
|
73
|
+
disabledReason: ( localize(5092, "Checking repository access...")),
|
|
74
|
+
pending: true
|
|
75
|
+
});
|
|
76
|
+
this.targetEligibility.set(key, result);
|
|
77
|
+
const signal = this.lifetime.signal;
|
|
78
|
+
void this.targetEligibilityLimiter.queue(async () => {
|
|
79
|
+
try {
|
|
80
|
+
this.requireAccount();
|
|
81
|
+
signal.throwIfAborted();
|
|
82
|
+
const repository = await this.resolveRepository(workspace);
|
|
83
|
+
signal.throwIfAborted();
|
|
84
|
+
const eligible = repository && (await this.withClient(
|
|
85
|
+
signal,
|
|
86
|
+
async (client, identity, signal) => (await client.query.getRepository({
|
|
87
|
+
...identity,
|
|
88
|
+
owner: repository.owner,
|
|
89
|
+
repo: repository.name
|
|
90
|
+
}, signal)).private
|
|
91
|
+
));
|
|
92
|
+
signal.throwIfAborted();
|
|
93
|
+
if (this.targetEligibility.get(key) !== result) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
result.set(eligible ? {
|
|
97
|
+
workspace: ( URI.from({
|
|
98
|
+
scheme: GITHUB_REMOTE_FILE_SCHEME,
|
|
99
|
+
authority: "github",
|
|
100
|
+
path: `/${repository.owner}/${repository.name}/HEAD`
|
|
101
|
+
}))
|
|
102
|
+
} : {
|
|
103
|
+
disabledReason: repository ? ( localize(5093, "This repository must be private")) : required,
|
|
104
|
+
isPublicRepository: repository !== undefined
|
|
105
|
+
}, undefined);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (!isCancellationError(error)) {
|
|
108
|
+
this.logService.warn("[CloudAutomations] Repository eligibility check failed", error);
|
|
109
|
+
}
|
|
110
|
+
if (!signal.aborted && !this._store.isDisposed && this.targetEligibility.get(key) === result) {
|
|
111
|
+
result.set({
|
|
112
|
+
disabledReason: ( localize(
|
|
113
|
+
5094,
|
|
114
|
+
"Unable to verify repository access. Refresh automations to try again."
|
|
115
|
+
))
|
|
116
|
+
}, undefined);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
loadTools() {
|
|
123
|
+
const current = this.cachedTools.get();
|
|
124
|
+
if (current?.kind === "loading" || current?.kind === "ready") {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const signal = this.lifetime.signal;
|
|
128
|
+
this.cachedTools.set({
|
|
129
|
+
kind: "loading"
|
|
130
|
+
}, undefined);
|
|
131
|
+
void (async () => {
|
|
132
|
+
try {
|
|
133
|
+
this.requireAccount();
|
|
134
|
+
const groups = await this.withClient(
|
|
135
|
+
signal,
|
|
136
|
+
(client, _identity, signal) => client.automations.listTools(signal)
|
|
137
|
+
);
|
|
138
|
+
signal.throwIfAborted();
|
|
139
|
+
this.cachedTools.set({
|
|
140
|
+
kind: "ready",
|
|
141
|
+
groups
|
|
142
|
+
}, undefined);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (signal.aborted || this._store.isDisposed) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!isCancellationError(error)) {
|
|
148
|
+
this.logService.warn("[CloudAutomations] Failed to load automation tools", error);
|
|
149
|
+
}
|
|
150
|
+
this.cachedTools.set({
|
|
151
|
+
kind: "error"
|
|
152
|
+
}, undefined);
|
|
153
|
+
}
|
|
154
|
+
})();
|
|
155
|
+
}
|
|
156
|
+
clearTargetEligibility() {
|
|
157
|
+
const targets = [...( this.targetEligibility.values())];
|
|
158
|
+
this.targetEligibility.clear();
|
|
159
|
+
for (const target of targets) {
|
|
160
|
+
target.set({
|
|
161
|
+
disabledReason: ( localize(5095, "Repository access must be verified again."))
|
|
162
|
+
}, undefined);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async registerRepository(workspace) {
|
|
166
|
+
const account = this.requireAccount();
|
|
167
|
+
const signal = this.lifetime.signal;
|
|
168
|
+
const repository = await this.resolveRepository(workspace);
|
|
169
|
+
signal.throwIfAborted();
|
|
170
|
+
if (!repository) {
|
|
171
|
+
throw ( new Error(( localize(5096, "Select a GitHub.com repository for this cloud automation."))));
|
|
172
|
+
}
|
|
173
|
+
await this.withClient(signal, async (client, identity, signal) => {
|
|
174
|
+
if (!(await client.query.getRepository({
|
|
175
|
+
...identity,
|
|
176
|
+
owner: repository.owner,
|
|
177
|
+
repo: repository.name
|
|
178
|
+
}, signal)).private) {
|
|
179
|
+
throw ( new Error(( localize(5097, "Cloud automations currently require a private GitHub repository."))));
|
|
180
|
+
}
|
|
181
|
+
this.rememberRepositories(account.accountName, [repository], signal);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async refresh() {
|
|
185
|
+
const account = this.requireAccount();
|
|
186
|
+
if (this.refreshPromise) {
|
|
187
|
+
return this.refreshPromise;
|
|
188
|
+
}
|
|
189
|
+
this.clearTargetEligibility();
|
|
190
|
+
const signal = this.lifetime.signal;
|
|
191
|
+
this.state.set("loading", undefined);
|
|
192
|
+
const refresh = this.operations.queue(async () => {
|
|
193
|
+
await this.withClient(
|
|
194
|
+
signal,
|
|
195
|
+
(client, identity, signal) => this.refreshRepositories(account, signal, client, identity)
|
|
196
|
+
);
|
|
197
|
+
signal.throwIfAborted();
|
|
198
|
+
this.uncertain.set(false, undefined);
|
|
199
|
+
});
|
|
200
|
+
this.refreshPromise = refresh;
|
|
201
|
+
try {
|
|
202
|
+
await refresh;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (!signal.aborted && !this._store.isDisposed) {
|
|
205
|
+
this.state.set("error", undefined);
|
|
206
|
+
this.logService.warn("[CloudAutomations] Failed to refresh repositories", error);
|
|
207
|
+
}
|
|
208
|
+
throw error;
|
|
209
|
+
} finally {
|
|
210
|
+
if (this.refreshPromise === refresh) {
|
|
211
|
+
this.refreshPromise = undefined;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async acquireClient(signal) {
|
|
216
|
+
const reference = await this.gitHubService.acquireDefaultAccountClient(signal);
|
|
217
|
+
try {
|
|
218
|
+
signal.throwIfAborted();
|
|
219
|
+
if (reference.object.endpoint.getApiBaseUri() !== "https://api.github.com") {
|
|
220
|
+
throw ( new Error(( localize(5098, "Cloud automations require a GitHub.com account."))));
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
reference.dispose();
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
this.clientStore.add(reference);
|
|
227
|
+
this.clientStore.add(reference.object.onDidInvalidate(() => this.reset()));
|
|
228
|
+
return reference.object;
|
|
229
|
+
}
|
|
230
|
+
async withClient(signal, task) {
|
|
231
|
+
signal.throwIfAborted();
|
|
232
|
+
const pending = this.clientPromise ??= this.acquireClient(signal);
|
|
233
|
+
let client;
|
|
234
|
+
try {
|
|
235
|
+
client = await pending;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (this.clientPromise === pending) {
|
|
238
|
+
this.clientPromise = undefined;
|
|
239
|
+
}
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
const credential = await client.credentials.getCredential(signal);
|
|
243
|
+
const operationSignal = AbortSignal.any([signal, credential.signal]);
|
|
244
|
+
try {
|
|
245
|
+
operationSignal.throwIfAborted();
|
|
246
|
+
const result = await task(client, credential.account, operationSignal);
|
|
247
|
+
operationSignal.throwIfAborted();
|
|
248
|
+
return result;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (operationSignal.aborted && !(error instanceof MutationUncertainError || error instanceof ApiRequestError && error.outcome === "indeterminate")) {
|
|
251
|
+
throw ( new CancellationError());
|
|
252
|
+
}
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async create(workspace, value, guard) {
|
|
257
|
+
const account = this.requireAccount();
|
|
258
|
+
const signal = this.lifetime.signal;
|
|
259
|
+
const repository = await this.resolveRepository(workspace);
|
|
260
|
+
signal.throwIfAborted();
|
|
261
|
+
if (!repository) {
|
|
262
|
+
throw ( new Error(( localize(5096, "Select a GitHub.com repository for this cloud automation."))));
|
|
263
|
+
}
|
|
264
|
+
return this.mutate(repository, async (client, ref, signal) => {
|
|
265
|
+
this.rememberRepositories(account.accountName, [repository], signal);
|
|
266
|
+
guard?.();
|
|
267
|
+
const definition = await client.automations.create(ref, value, signal);
|
|
268
|
+
return this.publish(repository, definition, signal);
|
|
269
|
+
}, CancellationToken.None, signal);
|
|
270
|
+
}
|
|
271
|
+
update(entry, patch, guard) {
|
|
272
|
+
return this.mutate(entry.repository, async (client, ref, signal) => {
|
|
273
|
+
let current;
|
|
274
|
+
try {
|
|
275
|
+
current = await client.automations.get(ref, entry.definition.id, signal);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
if (!(error instanceof ApiRequestError) || error.statusCode !== 404) {
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
this.removeEntry(entry, signal);
|
|
281
|
+
return {
|
|
282
|
+
entry: undefined,
|
|
283
|
+
updated: false
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
this.publish(entry.repository, current, signal);
|
|
287
|
+
const value = patch(current);
|
|
288
|
+
if (!value) {
|
|
289
|
+
return {
|
|
290
|
+
entry: {
|
|
291
|
+
repository: entry.repository,
|
|
292
|
+
definition: current
|
|
293
|
+
},
|
|
294
|
+
updated: false
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
guard?.();
|
|
298
|
+
const definition = await client.automations.update(ref, current.id, value, signal);
|
|
299
|
+
return {
|
|
300
|
+
entry: this.publish(entry.repository, definition, signal),
|
|
301
|
+
updated: true
|
|
302
|
+
};
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async delete(entry, guard) {
|
|
306
|
+
await this.mutate(entry.repository, async (client, ref, signal) => {
|
|
307
|
+
guard?.();
|
|
308
|
+
await client.automations.delete(ref, entry.definition.id, signal);
|
|
309
|
+
this.removeEntry(entry, signal);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
async run(entry, token = CancellationToken.None) {
|
|
313
|
+
await this.mutate(
|
|
314
|
+
entry.repository,
|
|
315
|
+
(client, ref, signal) => client.automations.dispatch(ref, entry.definition.id, {
|
|
316
|
+
event: "manual"
|
|
317
|
+
}, signal),
|
|
318
|
+
token
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
async stop(entry) {
|
|
322
|
+
await this.mutate(
|
|
323
|
+
entry.entry.repository,
|
|
324
|
+
(client, _ref, signal) => client.tasks.abort(entry.task.id, signal)
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
async refreshHistory() {
|
|
328
|
+
this.requireAccount();
|
|
329
|
+
const signal = this.lifetime.signal;
|
|
330
|
+
await this.operations.queue(async () => {
|
|
331
|
+
await this.withClient(signal, async (client, _identity, operationSignal) => {
|
|
332
|
+
const resources = ( new DisposableStore());
|
|
333
|
+
const controller = ( new AbortController());
|
|
334
|
+
const historySignal = AbortSignal.any([operationSignal, controller.signal]);
|
|
335
|
+
const limiter = resources.add(( new Limiter(4)));
|
|
336
|
+
try {
|
|
337
|
+
const history = await Promise.all(this.cachedEntries.get().map(entry => limiter.queue(async () => {
|
|
338
|
+
try {
|
|
339
|
+
historySignal.throwIfAborted();
|
|
340
|
+
const tasks = await client.automations.listRuns(entry.definition.id, historySignal, {
|
|
341
|
+
per_page: 50,
|
|
342
|
+
page: 1,
|
|
343
|
+
sort: "created_at",
|
|
344
|
+
direction: "desc",
|
|
345
|
+
is_archived: false
|
|
346
|
+
}).catch(error => {
|
|
347
|
+
if (!(error instanceof ApiRequestError) || error.statusCode !== 404) {
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
this.removeEntry(entry, historySignal);
|
|
351
|
+
this.logService.info("[CloudAutomations] Definition no longer available", entry.definition.id);
|
|
352
|
+
return undefined;
|
|
353
|
+
});
|
|
354
|
+
const result = [];
|
|
355
|
+
for (const task of tasks?.data.tasks ?? []) {
|
|
356
|
+
const detail = ["queued", "in_progress", "running", "waiting_for_user"].includes(task.state) ? await client.tasks.get(task.id, historySignal) : task;
|
|
357
|
+
result.push({
|
|
358
|
+
entry,
|
|
359
|
+
task: detail
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return result;
|
|
363
|
+
} catch (error) {
|
|
364
|
+
controller.abort(( new CancellationError()));
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
})));
|
|
368
|
+
operationSignal.throwIfAborted();
|
|
369
|
+
this.cachedHistory.set(history.flat(), undefined);
|
|
370
|
+
} finally {
|
|
371
|
+
controller.abort(( new CancellationError()));
|
|
372
|
+
resources.dispose();
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
mutate(
|
|
378
|
+
repository,
|
|
379
|
+
operation,
|
|
380
|
+
token = CancellationToken.None,
|
|
381
|
+
lifetime = this.lifetime.signal
|
|
382
|
+
) {
|
|
383
|
+
this.requireAccount();
|
|
384
|
+
return this.operations.queue(async () => {
|
|
385
|
+
lifetime.throwIfAborted();
|
|
386
|
+
if (this.uncertain.get()) {
|
|
387
|
+
throw ( new MutationUncertainError("unknown"));
|
|
388
|
+
}
|
|
389
|
+
const resources = ( new DisposableStore());
|
|
390
|
+
try {
|
|
391
|
+
const signal = AbortSignal.any([lifetime, toAbortSignal(token, resources)]);
|
|
392
|
+
return await this.withClient(signal, async (client, identity, signal) => {
|
|
393
|
+
const ref = {
|
|
394
|
+
...identity,
|
|
395
|
+
owner: repository.owner,
|
|
396
|
+
repo: repository.name
|
|
397
|
+
};
|
|
398
|
+
if (!(await client.query.getRepository(ref, signal)).private) {
|
|
399
|
+
throw ( new Error(( localize(5097, "Cloud automations currently require a private GitHub repository."))));
|
|
400
|
+
}
|
|
401
|
+
return operation(client, repository, signal);
|
|
402
|
+
});
|
|
403
|
+
} catch (error) {
|
|
404
|
+
if ((error instanceof MutationUncertainError || error instanceof ApiRequestError && error.outcome === "indeterminate") && !lifetime.aborted) {
|
|
405
|
+
this.uncertain.set(true, undefined);
|
|
406
|
+
}
|
|
407
|
+
throw error;
|
|
408
|
+
} finally {
|
|
409
|
+
resources.dispose();
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
publish(repository, definition, signal) {
|
|
414
|
+
signal.throwIfAborted();
|
|
415
|
+
const entry = {
|
|
416
|
+
repository,
|
|
417
|
+
definition
|
|
418
|
+
};
|
|
419
|
+
this.cachedEntries.set([
|
|
420
|
+
...this.cachedEntries.get().filter(candidate => !sameEntry(candidate, entry)),
|
|
421
|
+
entry
|
|
422
|
+
], undefined);
|
|
423
|
+
return entry;
|
|
424
|
+
}
|
|
425
|
+
removeEntry(entry, signal) {
|
|
426
|
+
signal.throwIfAborted();
|
|
427
|
+
transaction(tx => {
|
|
428
|
+
this.cachedEntries.set(
|
|
429
|
+
this.cachedEntries.get().filter(candidate => !sameEntry(candidate, entry)),
|
|
430
|
+
tx
|
|
431
|
+
);
|
|
432
|
+
this.cachedHistory.set(
|
|
433
|
+
this.cachedHistory.get().filter(candidate => !sameEntry(candidate.entry, entry)),
|
|
434
|
+
tx
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
async refreshRepositories(account, signal, client, identity) {
|
|
439
|
+
const repositories = this.readRepositories(account.accountName);
|
|
440
|
+
const errors = [];
|
|
441
|
+
for (const recent of this.recentWorkspacesService.getRecentWorkspaces(false)) {
|
|
442
|
+
const root = recent.workspace.folders[0]?.root;
|
|
443
|
+
try {
|
|
444
|
+
const repository = root && (await this.resolveRepository(root));
|
|
445
|
+
signal.throwIfAborted();
|
|
446
|
+
if (repository) {
|
|
447
|
+
repositories.set(repositoryKey(repository), repository);
|
|
448
|
+
}
|
|
449
|
+
} catch (error) {
|
|
450
|
+
signal.throwIfAborted();
|
|
451
|
+
this.logService.warn("[CloudAutomations] Failed to resolve a recent repository", error);
|
|
452
|
+
errors.push(error);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const snapshot = ( new Map());
|
|
456
|
+
for (const entry of this.cachedEntries.get()) {
|
|
457
|
+
const key = repositoryKey(entry.repository);
|
|
458
|
+
snapshot.set(key, [...(snapshot.get(key) ?? []), entry]);
|
|
459
|
+
}
|
|
460
|
+
const eligible = [];
|
|
461
|
+
for (const [key, repository] of repositories) {
|
|
462
|
+
try {
|
|
463
|
+
const ref = {
|
|
464
|
+
...identity,
|
|
465
|
+
owner: repository.owner,
|
|
466
|
+
repo: repository.name
|
|
467
|
+
};
|
|
468
|
+
if (!(await client.query.getRepository(ref, signal)).private) {
|
|
469
|
+
snapshot.delete(key);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
eligible.push(repository);
|
|
473
|
+
const definitions = await this.listDefinitions(client.automations, repository, signal);
|
|
474
|
+
snapshot.set(key, definitions.map(definition => ({
|
|
475
|
+
repository,
|
|
476
|
+
definition
|
|
477
|
+
})));
|
|
478
|
+
} catch (error) {
|
|
479
|
+
signal.throwIfAborted();
|
|
480
|
+
if (isCancellationError(error)) {
|
|
481
|
+
throw error;
|
|
482
|
+
}
|
|
483
|
+
this.logService.warn(`[CloudAutomations] Failed to refresh ${key}`, error);
|
|
484
|
+
errors.push(error);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
this.rememberRepositories(account.accountName, eligible, signal);
|
|
488
|
+
transaction(tx => {
|
|
489
|
+
this.cachedEntries.set([...( snapshot.values())].flat(), tx);
|
|
490
|
+
this.cachedHistory.set(this.cachedHistory.get().filter(row => ( this.cachedEntries.get().some(entry => sameEntry(row.entry, entry)))), tx);
|
|
491
|
+
this.state.set(errors.length ? "error" : "ready", tx);
|
|
492
|
+
});
|
|
493
|
+
if (errors.length) {
|
|
494
|
+
throw ( new AggregateError(errors, ( localize(
|
|
495
|
+
5099,
|
|
496
|
+
"Some GitHub repositories could not be refreshed. Check the logs for details."
|
|
497
|
+
))));
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
async listDefinitions(api, repository, signal) {
|
|
501
|
+
const controller = ( new AbortController());
|
|
502
|
+
const operationSignal = AbortSignal.any([signal, controller.signal]);
|
|
503
|
+
try {
|
|
504
|
+
const definitions = [];
|
|
505
|
+
for (let page = 1; page <= 10; page++) {
|
|
506
|
+
operationSignal.throwIfAborted();
|
|
507
|
+
const response = await api.list(repository, operationSignal, {
|
|
508
|
+
ownership: "user",
|
|
509
|
+
page,
|
|
510
|
+
per_page: 100
|
|
511
|
+
});
|
|
512
|
+
const summaries = response.data.automations;
|
|
513
|
+
for (let offset = 0; offset < summaries.length; offset += 5) {
|
|
514
|
+
operationSignal.throwIfAborted();
|
|
515
|
+
definitions.push(...(await Promise.all(
|
|
516
|
+
summaries.slice(offset, offset + 5).map(summary => api.get(repository, summary.id, operationSignal))
|
|
517
|
+
)));
|
|
518
|
+
}
|
|
519
|
+
if (!response.nextLink) {
|
|
520
|
+
return definitions;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
throw ( new Error(( localize(
|
|
524
|
+
5100,
|
|
525
|
+
"This repository has more cloud automations than can be loaded. Its catalogue is incomplete."
|
|
526
|
+
))));
|
|
527
|
+
} finally {
|
|
528
|
+
controller.abort();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
reset() {
|
|
532
|
+
this.lifetime.abort(( new CancellationError()));
|
|
533
|
+
this.clearTargetEligibility();
|
|
534
|
+
this.lifetime = ( new AbortController());
|
|
535
|
+
this.clientPromise = undefined;
|
|
536
|
+
this.clientStore.clear();
|
|
537
|
+
this.refreshPromise = undefined;
|
|
538
|
+
this.operations = ( new Sequencer());
|
|
539
|
+
const account = this.defaultAccountService.currentDefaultAccount;
|
|
540
|
+
transaction(tx => {
|
|
541
|
+
this.cachedEntries.set([], tx);
|
|
542
|
+
this.cachedHistory.set([], tx);
|
|
543
|
+
this.uncertain.set(false, tx);
|
|
544
|
+
this.cachedTools.set(undefined, tx);
|
|
545
|
+
this.state.set(account && !account.enterprise ? "loading" : "unavailable", tx);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
dispose() {
|
|
549
|
+
this.lifetime.abort(( new CancellationError()));
|
|
550
|
+
this.clearTargetEligibility();
|
|
551
|
+
super.dispose();
|
|
552
|
+
transaction(tx => {
|
|
553
|
+
this.cachedEntries.set([], tx);
|
|
554
|
+
this.cachedHistory.set([], tx);
|
|
555
|
+
this.state.set("unavailable", tx);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
requireAccount() {
|
|
559
|
+
if (this._store.isDisposed) {
|
|
560
|
+
throw ( new CancellationError());
|
|
561
|
+
}
|
|
562
|
+
const account = this.defaultAccountService.currentDefaultAccount;
|
|
563
|
+
if (!account || account.enterprise) {
|
|
564
|
+
throw ( new Error(( localize(
|
|
565
|
+
5101,
|
|
566
|
+
"Sign in to GitHub.com with repository access to manage cloud automations."
|
|
567
|
+
))));
|
|
568
|
+
}
|
|
569
|
+
return account;
|
|
570
|
+
}
|
|
571
|
+
async resolveRepository(workspace) {
|
|
572
|
+
const uri = workspace.scheme === GITHUB_REMOTE_FILE_SCHEME ? workspace : await this.resolveRepositoryUri(workspace);
|
|
573
|
+
const match = uri?.scheme === GITHUB_REMOTE_FILE_SCHEME && uri.authority === "github" ? /^\/(?<owner>[^/]+)\/(?<name>[^/]+)(?:\/|$)/.exec(uri.path) : undefined;
|
|
574
|
+
return match?.groups ? {
|
|
575
|
+
owner: match.groups.owner,
|
|
576
|
+
name: match.groups.name
|
|
577
|
+
} : undefined;
|
|
578
|
+
}
|
|
579
|
+
readRepositories(account) {
|
|
580
|
+
const stored = this.storageService.get(this.storageKey(account), StorageScope.PROFILE);
|
|
581
|
+
const repositories = ( new Map());
|
|
582
|
+
if (stored !== undefined) {
|
|
583
|
+
const values = JSON.parse(stored);
|
|
584
|
+
if (!Array.isArray(values)) {
|
|
585
|
+
throw ( new Error(( localize(5102, "Invalid stored cloud automation repositories."))));
|
|
586
|
+
}
|
|
587
|
+
for (const value of values) {
|
|
588
|
+
if (!isObject(value) || typeof value.owner !== "string" || !value.owner || value.owner.includes("/") || typeof value.name !== "string" || !value.name || value.name.includes("/")) {
|
|
589
|
+
throw ( new Error(( localize(5103, "Invalid stored cloud automation repository."))));
|
|
590
|
+
}
|
|
591
|
+
const repository = {
|
|
592
|
+
owner: value.owner,
|
|
593
|
+
name: value.name
|
|
594
|
+
};
|
|
595
|
+
repositories.set(repositoryKey(repository), repository);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return repositories;
|
|
599
|
+
}
|
|
600
|
+
rememberRepositories(account, repositories, signal) {
|
|
601
|
+
signal.throwIfAborted();
|
|
602
|
+
const known = this.readRepositories(account);
|
|
603
|
+
for (const repository of repositories) {
|
|
604
|
+
known.set(repositoryKey(repository), repository);
|
|
605
|
+
}
|
|
606
|
+
this.storageService.store(this.storageKey(account), JSON.stringify([...( known.values())]), StorageScope.PROFILE, StorageTarget.MACHINE);
|
|
607
|
+
}
|
|
608
|
+
storageKey(account) {
|
|
609
|
+
return `${REPOSITORIES_STORAGE_KEY}.${encodeURIComponent(account)}`;
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
GitHubCloudAutomationStore = ( __decorate([( __param(1, IWorkbenchGitHubService)), ( __param(2, IDefaultAccountService)), ( __param(3, IStorageService)), ( __param(4, ILogService)), ( __param(5, ISessionsRecentWorkspacesService))], GitHubCloudAutomationStore));
|
|
613
|
+
function repositoryKey(repository) {
|
|
614
|
+
return `${repository.owner}/${repository.name}`.toLowerCase();
|
|
615
|
+
}
|
|
616
|
+
function sameEntry(a, b) {
|
|
617
|
+
return a.definition.id === b.definition.id && repositoryKey(a.repository) === repositoryKey(b.repository);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export { GitHubCloudAutomationStore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
import { registerWorkbenchContribution2, WorkbenchPhase } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/contributions';
|
|
3
|
+
import { CloudSandboxAgentHostContribution } from './cloudSandboxAgentHostContribution.js';
|
|
4
|
+
|
|
5
|
+
registerWorkbenchContribution2(CloudSandboxAgentHostContribution.ID, CloudSandboxAgentHostContribution, WorkbenchPhase.AfterRestored);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CancellationToken } from "@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation";
|
|
2
|
+
import { DisposableStore, IDisposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle";
|
|
3
|
+
import { IAgentHostService } from "@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/agentService.service";
|
|
4
|
+
import { ICloudSandboxCreatedSession, ICloudSandboxCreateSessionRequest } from "@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/cloudSandboxAgentHost";
|
|
5
|
+
import { ICloudSandboxAgentHostService } from "@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/cloudSandboxAgentHost.service";
|
|
6
|
+
import { ICloudSandboxApiService } from "@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/cloudSandboxAgentHost.service";
|
|
7
|
+
import { IRemoteAgentHostService } from "@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/remoteAgentHostService.service";
|
|
8
|
+
import { IConfigurationService } from "@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service";
|
|
9
|
+
import { IInstantiationService } from "@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation";
|
|
10
|
+
import { ILogService } from "@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service";
|
|
11
|
+
import { IProgress } from "@codingame/monaco-vscode-api/vscode/vs/platform/progress/common/progress";
|
|
12
|
+
import { IStorageService } from "@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage.service";
|
|
13
|
+
import { IWorkspaceTrustManagementService } from "@codingame/monaco-vscode-api/vscode/vs/platform/workspace/common/workspaceTrust.service";
|
|
14
|
+
import { IChatSessionsService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/common/chatSessionsService.service";
|
|
15
|
+
import { CloudSandboxSessionContribution, ICloudSandboxSessionEnvironment } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/browser/remoteAgentHost/cloudSandboxSessionContribution";
|
|
16
|
+
import { IRemoteAgentHostConnectionCustomizationService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/browser/remoteAgentHost/remoteAgentHostConnectionCustomization.service";
|
|
17
|
+
import { IChatEntitlementService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/chat/common/chatEntitlementService.service";
|
|
18
|
+
import { IHostService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/host/browser/host.service";
|
|
19
|
+
import { IAgentHostFilterService } from "@codingame/monaco-vscode-api/vscode/vs/sessions/services/agentHostFilter/common/agentHostFilter.service";
|
|
20
|
+
import { ISessionsProvidersService } from "@codingame/monaco-vscode-api/vscode/vs/sessions/services/sessions/browser/sessionsProvidersService.service";
|
|
21
|
+
import { ISession } from "@codingame/monaco-vscode-api/vscode/vs/sessions/services/sessions/common/session";
|
|
22
|
+
import { CloudSandboxSessionsProvider } from "./cloudSandboxSessionsProvider.js";
|
|
23
|
+
import { IRemoteAgentHostSessionsProviderConfig } from "@codingame/monaco-vscode-api/vscode/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostSessionsProvider";
|
|
24
|
+
export declare const CLOUD_SANDBOX_CREATION_PROVIDER_ID = "cloud-sandbox-creation";
|
|
25
|
+
export interface ICloudSandboxProvisionedSession extends ICloudSandboxCreatedSession {
|
|
26
|
+
readonly provider: CloudSandboxSessionsProvider;
|
|
27
|
+
readonly session: ISession;
|
|
28
|
+
}
|
|
29
|
+
export declare class CloudSandboxAgentHostContribution extends CloudSandboxSessionContribution<CloudSandboxSessionsProvider> {
|
|
30
|
+
private readonly _remoteService;
|
|
31
|
+
private readonly _sandboxConnectionCustomizations;
|
|
32
|
+
private readonly _sessionsProvidersService;
|
|
33
|
+
private readonly _agentHostFilterService;
|
|
34
|
+
static readonly ID = "workbench.contrib.cloudSandboxAgentHost";
|
|
35
|
+
protected get supportsBackgroundConnection(): boolean;
|
|
36
|
+
private readonly _hostGroupRegistration;
|
|
37
|
+
constructor(cloudSandboxService: ICloudSandboxAgentHostService, apiService: ICloudSandboxApiService, _remoteService: IRemoteAgentHostService, _sandboxConnectionCustomizations: IRemoteAgentHostConnectionCustomizationService, _sessionsProvidersService: ISessionsProvidersService, _agentHostFilterService: IAgentHostFilterService, configurationService: IConfigurationService, instantiationService: IInstantiationService, chatSessionsService: IChatSessionsService, logService: ILogService, chatEntitlementService: IChatEntitlementService, hostService: IHostService, storageService: IStorageService, workspaceTrustManagementService: IWorkspaceTrustManagementService, localAgentHostService: IAgentHostService);
|
|
38
|
+
prepareSession(environmentId: string, repoNwo: string, token: CancellationToken): Promise<void>;
|
|
39
|
+
protected _updateRegistration(): void;
|
|
40
|
+
protected _ensureProvider(env: ICloudSandboxSessionEnvironment): void;
|
|
41
|
+
protected _createProvider(env: ICloudSandboxSessionEnvironment, store: DisposableStore): CloudSandboxSessionsProvider;
|
|
42
|
+
protected _instantiateProvider(config: IRemoteAgentHostSessionsProviderConfig): CloudSandboxSessionsProvider;
|
|
43
|
+
provisionSession(request: ICloudSandboxCreateSessionRequest, token: CancellationToken, progress?: IProgress<string>): Promise<ICloudSandboxProvisionedSession>;
|
|
44
|
+
/** Observe repository setup until the caller finishes dispatching the first turn. */
|
|
45
|
+
trackSessionCreationProgress(environmentId: string, repoNwo: string, progress: IProgress<string>): IDisposable;
|
|
46
|
+
}
|