@vibesdotdev/code-sessions 0.0.0-stage → 1.0.0-rc.1
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/LICENSE +18 -0
- package/README.md +165 -2
- package/dist/agent/coordinator.agent.descriptor.d.ts +69 -0
- package/dist/agent/coordinator.agent.descriptor.d.ts.map +1 -0
- package/dist/agent/coordinator.agent.descriptor.js +60 -0
- package/dist/agent/coordinator.agent.descriptor.js.map +1 -0
- package/dist/agent/coordinator.prompt.descriptor.d.ts +135 -0
- package/dist/agent/coordinator.prompt.descriptor.d.ts.map +1 -0
- package/dist/agent/coordinator.prompt.descriptor.js +29 -0
- package/dist/agent/coordinator.prompt.descriptor.js.map +1 -0
- package/dist/agent/coordinator.system-prompt.d.ts +3 -0
- package/dist/agent/coordinator.system-prompt.d.ts.map +1 -0
- package/dist/agent/coordinator.system-prompt.js +18 -0
- package/dist/agent/coordinator.system-prompt.js.map +1 -0
- package/dist/agent/developer.agent.descriptor.d.ts +74 -0
- package/dist/agent/developer.agent.descriptor.d.ts.map +1 -0
- package/dist/agent/developer.agent.descriptor.js +138 -0
- package/dist/agent/developer.agent.descriptor.js.map +1 -0
- package/dist/agent/developer.prompt.descriptor.d.ts +137 -0
- package/dist/agent/developer.prompt.descriptor.d.ts.map +1 -0
- package/dist/agent/developer.prompt.descriptor.js +29 -0
- package/dist/agent/developer.prompt.descriptor.js.map +1 -0
- package/dist/agent/reviewer.agent.descriptor.d.ts +69 -0
- package/dist/agent/reviewer.agent.descriptor.d.ts.map +1 -0
- package/dist/agent/reviewer.agent.descriptor.js +60 -0
- package/dist/agent/reviewer.agent.descriptor.js.map +1 -0
- package/dist/agent/reviewer.prompt.descriptor.d.ts +135 -0
- package/dist/agent/reviewer.prompt.descriptor.d.ts.map +1 -0
- package/dist/agent/reviewer.prompt.descriptor.js +29 -0
- package/dist/agent/reviewer.prompt.descriptor.js.map +1 -0
- package/dist/agent/reviewer.system-prompt.d.ts +3 -0
- package/dist/agent/reviewer.system-prompt.d.ts.map +1 -0
- package/dist/agent/reviewer.system-prompt.js +17 -0
- package/dist/agent/reviewer.system-prompt.js.map +1 -0
- package/dist/agent/system-prompt.d.ts +12 -0
- package/dist/agent/system-prompt.d.ts.map +1 -0
- package/dist/agent/system-prompt.js +28 -0
- package/dist/agent/system-prompt.js.map +1 -0
- package/dist/api/code-sessions.api-client.descriptor.d.ts +25 -0
- package/dist/api/code-sessions.api-client.descriptor.d.ts.map +1 -0
- package/dist/api/code-sessions.api-client.descriptor.js +26 -0
- package/dist/api/code-sessions.api-client.descriptor.js.map +1 -0
- package/dist/api/code-sessions.api-client.impl.d.ts +156 -0
- package/dist/api/code-sessions.api-client.impl.d.ts.map +1 -0
- package/dist/api/code-sessions.api-client.impl.js +852 -0
- package/dist/api/code-sessions.api-client.impl.js.map +1 -0
- package/dist/api/code-sessions.api-client.schemas.d.ts +2 -0
- package/dist/api/code-sessions.api-client.schemas.d.ts.map +1 -0
- package/dist/api/code-sessions.api-client.schemas.js +2 -0
- package/dist/api/code-sessions.api-client.schemas.js.map +1 -0
- package/dist/api/code-sessions.api-client.types.d.ts +252 -0
- package/dist/api/code-sessions.api-client.types.d.ts.map +1 -0
- package/dist/api/code-sessions.api-client.types.js +77 -0
- package/dist/api/code-sessions.api-client.types.js.map +1 -0
- package/dist/api/index.d.ts +13 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +20 -0
- package/dist/api/index.js.map +1 -0
- package/dist/capability.d.ts +9 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +9 -0
- package/dist/capability.js.map +1 -0
- package/dist/catalog/index.d.ts +2 -0
- package/dist/catalog/index.d.ts.map +1 -0
- package/dist/catalog/index.js +2 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/catalog/session-catalog.d.ts +131 -0
- package/dist/catalog/session-catalog.d.ts.map +1 -0
- package/dist/catalog/session-catalog.impl.consumer.worker.d.ts +113 -0
- package/dist/catalog/session-catalog.impl.consumer.worker.d.ts.map +1 -0
- package/dist/catalog/session-catalog.impl.consumer.worker.js +32 -0
- package/dist/catalog/session-catalog.impl.consumer.worker.js.map +1 -0
- package/dist/catalog/session-catalog.js +126 -0
- package/dist/catalog/session-catalog.js.map +1 -0
- package/dist/clock/clock.client.d.ts +18 -0
- package/dist/clock/clock.client.d.ts.map +1 -0
- package/dist/clock/clock.client.js +43 -0
- package/dist/clock/clock.client.js.map +1 -0
- package/dist/code-sessions.consumer.plugin.d.ts +4 -0
- package/dist/code-sessions.consumer.plugin.d.ts.map +1 -0
- package/dist/code-sessions.consumer.plugin.js +16 -0
- package/dist/code-sessions.consumer.plugin.js.map +1 -0
- package/dist/code-sessions.plugin.d.ts +15 -0
- package/dist/code-sessions.plugin.d.ts.map +1 -0
- package/dist/code-sessions.plugin.js +65 -0
- package/dist/code-sessions.plugin.js.map +1 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +9 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/preparation.d.ts +2 -0
- package/dist/config/preparation.d.ts.map +1 -0
- package/dist/config/preparation.js +2 -0
- package/dist/config/preparation.js.map +1 -0
- package/dist/docs/code-sessions.docs.descriptor.d.ts +4 -0
- package/dist/docs/code-sessions.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/code-sessions.docs.descriptor.js +241 -0
- package/dist/docs/code-sessions.docs.descriptor.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/pull-request-plan.d.ts +68 -0
- package/dist/pull-request-plan.d.ts.map +1 -0
- package/dist/pull-request-plan.js +94 -0
- package/dist/pull-request-plan.js.map +1 -0
- package/dist/schemas/preparation.d.ts +51 -0
- package/dist/schemas/preparation.d.ts.map +1 -0
- package/dist/schemas/preparation.js +20 -0
- package/dist/schemas/preparation.js.map +1 -0
- package/dist/schemas/requests.d.ts +411 -0
- package/dist/schemas/requests.d.ts.map +1 -0
- package/dist/schemas/requests.js +87 -0
- package/dist/schemas/requests.js.map +1 -0
- package/dist/schemas/responses.d.ts +275 -0
- package/dist/schemas/responses.d.ts.map +1 -0
- package/dist/schemas/responses.js +90 -0
- package/dist/schemas/responses.js.map +1 -0
- package/dist/svelte/actions.d.ts +70 -0
- package/dist/svelte/actions.d.ts.map +1 -0
- package/dist/svelte/actions.js +280 -0
- package/dist/svelte/clock.svelte.d.ts +6 -0
- package/dist/svelte/clock.svelte.d.ts.map +1 -0
- package/dist/svelte/clock.svelte.js +33 -0
- package/dist/svelte/components/CreateSessionDialog.svelte +470 -0
- package/dist/svelte/components/CreateSessionDialog.svelte.d.ts +57 -0
- package/dist/svelte/components/CreateSessionDialog.svelte.d.ts.map +1 -0
- package/dist/svelte/components/SessionCard.svelte +216 -0
- package/dist/svelte/components/SessionCard.svelte.d.ts +27 -0
- package/dist/svelte/components/SessionCard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/SessionList.svelte +86 -0
- package/dist/svelte/components/SessionList.svelte.d.ts +23 -0
- package/dist/svelte/components/SessionList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/SessionSeed.svelte +60 -0
- package/dist/svelte/components/SessionSeed.svelte.d.ts +9 -0
- package/dist/svelte/components/SessionSeed.svelte.d.ts.map +1 -0
- package/dist/svelte/components/TranscriptView.svelte +360 -0
- package/dist/svelte/components/TranscriptView.svelte.d.ts +26 -0
- package/dist/svelte/components/TranscriptView.svelte.d.ts.map +1 -0
- package/dist/svelte/create-session-form.d.ts +84 -0
- package/dist/svelte/create-session-form.d.ts.map +1 -0
- package/dist/svelte/create-session-form.js +107 -0
- package/dist/svelte/index.d.ts +20 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +18 -0
- package/dist/svelte/live-session.d.ts +156 -0
- package/dist/svelte/live-session.d.ts.map +1 -0
- package/dist/svelte/live-session.js +152 -0
- package/dist/svelte/loaders.d.ts +74 -0
- package/dist/svelte/loaders.d.ts.map +1 -0
- package/dist/svelte/loaders.js +124 -0
- package/dist/svelte/outcome.d.ts +46 -0
- package/dist/svelte/outcome.d.ts.map +1 -0
- package/dist/svelte/outcome.js +49 -0
- package/dist/svelte/relative-time.d.ts +32 -0
- package/dist/svelte/relative-time.d.ts.map +1 -0
- package/dist/svelte/relative-time.js +51 -0
- package/dist/svelte/session-list-item.d.ts +23 -0
- package/dist/svelte/session-list-item.d.ts.map +1 -0
- package/dist/svelte/session-list-item.js +1 -0
- package/dist/svelte/styles.css +8 -0
- package/dist/svelte/transcript-input.d.ts +45 -0
- package/dist/svelte/transcript-input.d.ts.map +1 -0
- package/dist/svelte/transcript-input.js +188 -0
- package/dist/tui/code-sessions-tui.plugin.d.ts +10 -0
- package/dist/tui/code-sessions-tui.plugin.d.ts.map +1 -0
- package/dist/tui/code-sessions-tui.plugin.js +29 -0
- package/dist/tui/code-sessions-tui.plugin.js.map +1 -0
- package/dist/tui/code-sessions.tui-app.descriptor.d.ts +7 -0
- package/dist/tui/code-sessions.tui-app.descriptor.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-app.descriptor.js +30 -0
- package/dist/tui/code-sessions.tui-app.descriptor.js.map +1 -0
- package/dist/tui/code-sessions.tui-app.impl.consumer.d.ts +14 -0
- package/dist/tui/code-sessions.tui-app.impl.consumer.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-app.impl.consumer.js +453 -0
- package/dist/tui/code-sessions.tui-app.impl.consumer.js.map +1 -0
- package/dist/tui/code-sessions.tui-client.d.ts +22 -0
- package/dist/tui/code-sessions.tui-client.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-client.js +95 -0
- package/dist/tui/code-sessions.tui-client.js.map +1 -0
- package/dist/tui/code-sessions.tui-open.descriptor.d.ts +7 -0
- package/dist/tui/code-sessions.tui-open.descriptor.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-open.descriptor.js +16 -0
- package/dist/tui/code-sessions.tui-open.descriptor.js.map +1 -0
- package/dist/tui/code-sessions.tui-open.impl.consumer.d.ts +7 -0
- package/dist/tui/code-sessions.tui-open.impl.consumer.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-open.impl.consumer.js +11 -0
- package/dist/tui/code-sessions.tui-open.impl.consumer.js.map +1 -0
- package/dist/tui/code-sessions.tui-state.descriptor.d.ts +16 -0
- package/dist/tui/code-sessions.tui-state.descriptor.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-state.descriptor.js +50 -0
- package/dist/tui/code-sessions.tui-state.descriptor.js.map +1 -0
- package/dist/tui/code-sessions.tui-state.impl.consumer.d.ts +30 -0
- package/dist/tui/code-sessions.tui-state.impl.consumer.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-state.impl.consumer.js +533 -0
- package/dist/tui/code-sessions.tui-state.impl.consumer.js.map +1 -0
- package/dist/tui/code-sessions.tui-state.key.d.ts +322 -0
- package/dist/tui/code-sessions.tui-state.key.d.ts.map +1 -0
- package/dist/tui/code-sessions.tui-state.key.js +104 -0
- package/dist/tui/code-sessions.tui-state.key.js.map +1 -0
- package/dist/tui/index.d.ts +9 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +8 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/workspace/index.d.ts +2 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +2 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/workspace-binding.d.ts +74 -0
- package/dist/workspace/workspace-binding.d.ts.map +1 -0
- package/dist/workspace/workspace-binding.impl.consumer.worker.d.ts +17 -0
- package/dist/workspace/workspace-binding.impl.consumer.worker.d.ts.map +1 -0
- package/dist/workspace/workspace-binding.impl.consumer.worker.js +134 -0
- package/dist/workspace/workspace-binding.impl.consumer.worker.js.map +1 -0
- package/dist/workspace/workspace-binding.js +72 -0
- package/dist/workspace/workspace-binding.js.map +1 -0
- package/package.json +238 -4
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { CodeSessionCreateInput, CodeSessionSeed } from '@vibesdotdev/code-sessions';
|
|
3
|
+
|
|
4
|
+
export type HostTargetOption = 'local' | 'fleet' | 'cloud';
|
|
5
|
+
|
|
6
|
+
export interface FleetHostOption {
|
|
7
|
+
hostId: string;
|
|
8
|
+
name: string;
|
|
9
|
+
online: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CreateSessionDialogProps {
|
|
13
|
+
open?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Task this session is being started for, when the user got here from one.
|
|
16
|
+
*
|
|
17
|
+
* Reuses the platform's existing `CodeSessionSeed` contract rather than a
|
|
18
|
+
* web-only shape: the work-items TUI already creates sessions with
|
|
19
|
+
* `{ source: 'work-item', title, ref }`, the platform threads it into the
|
|
20
|
+
* sandbox as `VIBES_SEED_*`, and the session record persists it verbatim.
|
|
21
|
+
* A second convention here would mean a session started from the web could
|
|
22
|
+
* not be traced back the way one started from the TUI can.
|
|
23
|
+
*/
|
|
24
|
+
seed?: CodeSessionSeed;
|
|
25
|
+
/** Called when the user submits the form; parent handles the actual create call. */
|
|
26
|
+
oncreate?: (input: CodeSessionCreateInput) => void | Promise<void>;
|
|
27
|
+
/** Called when the dialog is closed. */
|
|
28
|
+
onclose?: () => void;
|
|
29
|
+
/** Repository options to populate the picker. */
|
|
30
|
+
repositories?: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
defaultBranch?: string | null;
|
|
34
|
+
description?: string | null;
|
|
35
|
+
group?: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** Whether the create action is in progress. */
|
|
38
|
+
creating?: boolean;
|
|
39
|
+
/** Configuration-only embeddings may continue to a separate dispatch step. */
|
|
40
|
+
submitLabel?: string;
|
|
41
|
+
/** Error from a failed create attempt. */
|
|
42
|
+
createError?: string | null;
|
|
43
|
+
/** Optional URL for creating a repository in the owning forge surface. */
|
|
44
|
+
repositoryCreateHref?: string;
|
|
45
|
+
repositoryCreateLabel?: string;
|
|
46
|
+
/** Hide scratch mode when the caller requires a repository-backed outcome. */
|
|
47
|
+
allowEphemeral?: boolean;
|
|
48
|
+
/** Let the user explicitly request repository provisioning at the caller's submit boundary. */
|
|
49
|
+
allowRepositoryCreation?: boolean;
|
|
50
|
+
/** Execution targets the embedding surface can actually provision. */
|
|
51
|
+
allowedHostTargets?: HostTargetOption[];
|
|
52
|
+
/**
|
|
53
|
+
* Fleet machines available for code execution (labeled sandbox:code-execution).
|
|
54
|
+
* When empty the "Fleet host" radio is disabled and an onboarding hint is shown.
|
|
55
|
+
*/
|
|
56
|
+
fleetHosts?: FleetHostOption[];
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<script lang="ts">
|
|
61
|
+
import * as Dialog from '@vibesdotdev/kit/components/ui/dialog';
|
|
62
|
+
import { Button } from '@vibesdotdev/kit/components/ui/button';
|
|
63
|
+
import { Input } from '@vibesdotdev/kit/components/ui/input';
|
|
64
|
+
import { Label } from '@vibesdotdev/kit/components/ui/label';
|
|
65
|
+
import { Select, SelectContent, SelectItem, SelectTrigger } from '@vibesdotdev/kit/components';
|
|
66
|
+
import { Icon } from '@vibesdotdev/kit/components';
|
|
67
|
+
import { SearchSelect } from '@vibesdotdev/kit/svelte';
|
|
68
|
+
import {
|
|
69
|
+
buildCreateSessionInput,
|
|
70
|
+
canSubmitCreateSession,
|
|
71
|
+
describeCreateSessionBlocker,
|
|
72
|
+
isSelectableFleetHost,
|
|
73
|
+
resolveDefaultHostTarget,
|
|
74
|
+
resolveDefaultFleetHostId,
|
|
75
|
+
resolveSelectableHostTargets
|
|
76
|
+
} from '../create-session-form.js';
|
|
77
|
+
|
|
78
|
+
let {
|
|
79
|
+
open = false,
|
|
80
|
+
seed = undefined,
|
|
81
|
+
oncreate,
|
|
82
|
+
onclose,
|
|
83
|
+
repositories = [],
|
|
84
|
+
creating = false,
|
|
85
|
+
submitLabel = 'Start session',
|
|
86
|
+
createError = null,
|
|
87
|
+
repositoryCreateHref = undefined,
|
|
88
|
+
repositoryCreateLabel = 'Create git.vibes.dev repository',
|
|
89
|
+
allowEphemeral = true,
|
|
90
|
+
allowRepositoryCreation = false,
|
|
91
|
+
allowedHostTargets = ['cloud', 'local', 'fleet'],
|
|
92
|
+
fleetHosts = []
|
|
93
|
+
}: CreateSessionDialogProps = $props();
|
|
94
|
+
|
|
95
|
+
// --- Form state ---
|
|
96
|
+
let selectedHostTarget: HostTargetOption = $state('cloud');
|
|
97
|
+
let selectedRepositoryId: string = $state('');
|
|
98
|
+
let checkoutRef: string = $state('');
|
|
99
|
+
let ephemeral: boolean = $state(false);
|
|
100
|
+
let createRepositoryIfMissing: boolean = $state(false);
|
|
101
|
+
const defaultFleetHostId = $derived(resolveDefaultFleetHostId(fleetHosts));
|
|
102
|
+
const hasOnlineFleetHosts = $derived(defaultFleetHostId.length > 0);
|
|
103
|
+
let selectedFleetHostId: string = $state('');
|
|
104
|
+
|
|
105
|
+
const selectedRepo = $derived(repositories.find((r) => r.id === selectedRepositoryId));
|
|
106
|
+
const selectedFleetHost = $derived(fleetHosts.find((h) => h.hostId === selectedFleetHostId));
|
|
107
|
+
const selectableHostTargets = $derived(resolveSelectableHostTargets(allowedHostTargets, fleetHosts));
|
|
108
|
+
const defaultHostTarget = $derived(resolveDefaultHostTarget(selectableHostTargets));
|
|
109
|
+
const canStartSession = $derived(
|
|
110
|
+
canSubmitCreateSession({
|
|
111
|
+
ephemeral,
|
|
112
|
+
selectedRepositoryId,
|
|
113
|
+
createRepositoryIfMissing,
|
|
114
|
+
selectedHostTarget,
|
|
115
|
+
allowedHostTargets: selectableHostTargets,
|
|
116
|
+
selectedFleetHostId
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
// Why the submit button is disabled. Never null while `canStartSession` is
|
|
120
|
+
// false — the two are pinned to agree by test.
|
|
121
|
+
const submitBlocker = $derived(
|
|
122
|
+
describeCreateSessionBlocker({
|
|
123
|
+
ephemeral,
|
|
124
|
+
selectedRepositoryId,
|
|
125
|
+
createRepositoryIfMissing,
|
|
126
|
+
selectedHostTarget,
|
|
127
|
+
allowedHostTargets: selectableHostTargets,
|
|
128
|
+
selectedFleetHostId,
|
|
129
|
+
repositoryCount: repositories.length,
|
|
130
|
+
repositoryCreationAvailable: allowRepositoryCreation
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
function handleOpenChange(value: boolean) {
|
|
135
|
+
if (!value) onclose?.();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function resetForm() {
|
|
139
|
+
if (defaultHostTarget) selectedHostTarget = defaultHostTarget;
|
|
140
|
+
selectedRepositoryId = '';
|
|
141
|
+
checkoutRef = '';
|
|
142
|
+
ephemeral = false;
|
|
143
|
+
createRepositoryIfMissing = false;
|
|
144
|
+
selectedFleetHostId = defaultFleetHostId;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
$effect(() => {
|
|
148
|
+
if (!allowEphemeral && ephemeral) ephemeral = false;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
$effect(() => {
|
|
152
|
+
if (!allowRepositoryCreation || repositories.length > 0) createRepositoryIfMissing = false;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
$effect(() => {
|
|
156
|
+
if (!isSelectableFleetHost(fleetHosts, selectedFleetHostId)) {
|
|
157
|
+
selectedFleetHostId = defaultFleetHostId;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
$effect(() => {
|
|
162
|
+
if (!allowedHostTargets.includes(selectedHostTarget)) {
|
|
163
|
+
if (defaultHostTarget) selectedHostTarget = defaultHostTarget;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
$effect(() => {
|
|
168
|
+
if (selectedHostTarget === 'fleet' && !hasOnlineFleetHosts) {
|
|
169
|
+
if (defaultHostTarget) selectedHostTarget = defaultHostTarget;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
async function handleSubmit(e: SubmitEvent) {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
if (!canStartSession) return;
|
|
176
|
+
|
|
177
|
+
let hostTarget: CodeSessionCreateInput['hostTarget'];
|
|
178
|
+
if (selectedHostTarget === 'cloud') {
|
|
179
|
+
hostTarget = { kind: 'cloud' };
|
|
180
|
+
} else if (selectedHostTarget === 'local') {
|
|
181
|
+
hostTarget = { kind: 'host', hostId: 'local' };
|
|
182
|
+
} else {
|
|
183
|
+
// fleet — use the real hostId the user selected
|
|
184
|
+
hostTarget = { kind: 'host', hostId: selectedFleetHostId };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Built by the pure helper so the contract — including that the seed
|
|
188
|
+
// survives the trip — is pinned by a test rather than by this component.
|
|
189
|
+
const input: CodeSessionCreateInput = buildCreateSessionInput({
|
|
190
|
+
hostTarget,
|
|
191
|
+
ephemeral,
|
|
192
|
+
selectedRepositoryId,
|
|
193
|
+
createRepositoryIfMissing,
|
|
194
|
+
checkoutRef,
|
|
195
|
+
seed
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
await oncreate?.(input);
|
|
199
|
+
if (!createError) {
|
|
200
|
+
resetForm();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const hostOptions: Array<{ value: HostTargetOption; label: string; description: string }> = [
|
|
205
|
+
{
|
|
206
|
+
value: 'cloud',
|
|
207
|
+
label: 'Cloud sandbox',
|
|
208
|
+
description: 'Runs in a managed cloud environment. Snapshot execution — re-runnable commands.'
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
value: 'local',
|
|
212
|
+
label: 'Local machine',
|
|
213
|
+
description: 'Runs on your local machine. Full interactive terminal.'
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
value: 'fleet',
|
|
217
|
+
label: 'Connected machine',
|
|
218
|
+
description: 'Runs on a paired fleet machine. Full interactive terminal.'
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
const visibleHostOptions = $derived(
|
|
222
|
+
hostOptions.filter((option) => allowedHostTargets.includes(option.value))
|
|
223
|
+
);
|
|
224
|
+
</script>
|
|
225
|
+
|
|
226
|
+
<!--
|
|
227
|
+
CreateSessionDialog — host-target picker with repository + ref fields.
|
|
228
|
+
No internal jargon. Destructive actions confirm with consequences stated.
|
|
229
|
+
-->
|
|
230
|
+
<Dialog.Root {open} onOpenChange={handleOpenChange}>
|
|
231
|
+
<Dialog.Content class="sm:max-w-lg">
|
|
232
|
+
<Dialog.Header>
|
|
233
|
+
<Dialog.Title>New code session</Dialog.Title>
|
|
234
|
+
<Dialog.Description>
|
|
235
|
+
Choose where to run your session and which repository to clone.
|
|
236
|
+
</Dialog.Description>
|
|
237
|
+
</Dialog.Header>
|
|
238
|
+
|
|
239
|
+
{#if seed}
|
|
240
|
+
<!--
|
|
241
|
+
Shown, not hidden: the person needs to see which task this session will
|
|
242
|
+
be started for before they commit a sandbox to it.
|
|
243
|
+
-->
|
|
244
|
+
<div class="rounded-lg border bg-muted/40 p-3">
|
|
245
|
+
<p class="text-xs text-muted-foreground">Working on</p>
|
|
246
|
+
<p class="mt-1 font-medium leading-snug">{seed.title}</p>
|
|
247
|
+
{#if seed.body}
|
|
248
|
+
<p class="mt-1 text-sm text-muted-foreground line-clamp-3">{seed.body}</p>
|
|
249
|
+
{/if}
|
|
250
|
+
</div>
|
|
251
|
+
{/if}
|
|
252
|
+
|
|
253
|
+
<form onsubmit={handleSubmit} class="space-y-5 pt-2">
|
|
254
|
+
<!-- Host target picker -->
|
|
255
|
+
<!--
|
|
256
|
+
A real radio group. These three radios previously sat in a bare
|
|
257
|
+
<div> under a <Label> that labelled nothing, so assistive tech
|
|
258
|
+
announced three orphaned controls with no group name.
|
|
259
|
+
-->
|
|
260
|
+
<div class="space-y-2" role="radiogroup" aria-labelledby="host-target-label">
|
|
261
|
+
<Label id="host-target-label">Where to run</Label>
|
|
262
|
+
<div class="grid gap-2">
|
|
263
|
+
{#each visibleHostOptions as opt}
|
|
264
|
+
{@const isFleet = opt.value === 'fleet'}
|
|
265
|
+
{@const fleetDisabled = isFleet && !hasOnlineFleetHosts}
|
|
266
|
+
<label
|
|
267
|
+
class={[
|
|
268
|
+
'flex items-start gap-3 rounded-lg border p-3 transition-colors',
|
|
269
|
+
fleetDisabled
|
|
270
|
+
? 'cursor-not-allowed opacity-60'
|
|
271
|
+
: 'cursor-pointer hover:bg-muted/40',
|
|
272
|
+
selectedHostTarget === opt.value ? 'border-primary bg-primary/5' : ''
|
|
273
|
+
].join(' ')}
|
|
274
|
+
>
|
|
275
|
+
<input
|
|
276
|
+
type="radio"
|
|
277
|
+
name="hostTarget"
|
|
278
|
+
value={opt.value}
|
|
279
|
+
bind:group={selectedHostTarget}
|
|
280
|
+
disabled={fleetDisabled}
|
|
281
|
+
class="mt-0.5 accent-primary"
|
|
282
|
+
/>
|
|
283
|
+
<div class="flex-1 min-w-0">
|
|
284
|
+
<p class="font-medium leading-none">{opt.label}</p>
|
|
285
|
+
<p class="mt-1 text-sm text-muted-foreground">{opt.description}</p>
|
|
286
|
+
{#if isFleet && fleetHosts.length === 0}
|
|
287
|
+
<p class="mt-2 text-xs text-muted-foreground">
|
|
288
|
+
No machines are set up for code execution. Add one from Machines, then run:
|
|
289
|
+
<code class="font-mono">vibes fleet daemon --labels sandbox:code-execution</code
|
|
290
|
+
>
|
|
291
|
+
</p>
|
|
292
|
+
{:else if isFleet && !hasOnlineFleetHosts}
|
|
293
|
+
<p class="mt-2 text-xs text-muted-foreground">
|
|
294
|
+
No code-execution machines are online. Start a paired machine, then run:
|
|
295
|
+
<code class="font-mono">vibes fleet daemon --labels sandbox:code-execution</code
|
|
296
|
+
>
|
|
297
|
+
</p>
|
|
298
|
+
{/if}
|
|
299
|
+
{#if isFleet && selectedHostTarget === 'fleet' && fleetHosts.length > 0}
|
|
300
|
+
<!-- Machine picker shown inline when fleet is selected -->
|
|
301
|
+
<div class="mt-3">
|
|
302
|
+
<Select type="single" bind:value={selectedFleetHostId}>
|
|
303
|
+
<SelectTrigger class="w-full">
|
|
304
|
+
<!-- Same defect as the repository picker: `SelectValue` only
|
|
305
|
+
knows the hostId, so the trigger kept reading as empty
|
|
306
|
+
after a real choice. Fixed here too rather than left for
|
|
307
|
+
the next person to rediscover on a machine that has
|
|
308
|
+
fleet hosts to select. -->
|
|
309
|
+
{selectedFleetHost?.name ?? 'Choose a machine…'}
|
|
310
|
+
</SelectTrigger>
|
|
311
|
+
<SelectContent>
|
|
312
|
+
{#each fleetHosts as host}
|
|
313
|
+
<SelectItem value={host.hostId} disabled={!host.online}>
|
|
314
|
+
{host.name}
|
|
315
|
+
{#if !host.online}
|
|
316
|
+
<span class="ml-1 text-xs text-muted-foreground">(Offline)</span>
|
|
317
|
+
{/if}
|
|
318
|
+
</SelectItem>
|
|
319
|
+
{/each}
|
|
320
|
+
</SelectContent>
|
|
321
|
+
</Select>
|
|
322
|
+
</div>
|
|
323
|
+
{/if}
|
|
324
|
+
</div>
|
|
325
|
+
</label>
|
|
326
|
+
{/each}
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
|
|
330
|
+
{#if allowEphemeral}
|
|
331
|
+
<!-- Ephemeral toggle -->
|
|
332
|
+
<div class="flex items-center gap-3">
|
|
333
|
+
<input
|
|
334
|
+
id="ephemeral-toggle"
|
|
335
|
+
type="checkbox"
|
|
336
|
+
bind:checked={ephemeral}
|
|
337
|
+
class="accent-primary"
|
|
338
|
+
/>
|
|
339
|
+
<Label for="ephemeral-toggle" class="cursor-pointer">
|
|
340
|
+
Ephemeral workspace
|
|
341
|
+
<span class="ml-1 text-xs text-muted-foreground">(no repository — scratch space)</span>
|
|
342
|
+
</Label>
|
|
343
|
+
</div>
|
|
344
|
+
{/if}
|
|
345
|
+
|
|
346
|
+
{#if !ephemeral}
|
|
347
|
+
<!-- Repository selector -->
|
|
348
|
+
<div class="space-y-2">
|
|
349
|
+
<Label for="repo-select">Repository</Label>
|
|
350
|
+
{#if repositories.length > 0}
|
|
351
|
+
<SearchSelect
|
|
352
|
+
id="repo-select"
|
|
353
|
+
label="Repository"
|
|
354
|
+
options={repositories.map((repo) => ({
|
|
355
|
+
value: repo.id,
|
|
356
|
+
label: repo.name,
|
|
357
|
+
description: repo.description,
|
|
358
|
+
group: repo.group
|
|
359
|
+
}))}
|
|
360
|
+
bind:value={selectedRepositoryId}
|
|
361
|
+
placeholder="Select a repository…"
|
|
362
|
+
searchPlaceholder="Search repositories…"
|
|
363
|
+
emptyMessage="No repositories found"
|
|
364
|
+
disabled={creating}
|
|
365
|
+
/>
|
|
366
|
+
{:else}
|
|
367
|
+
<div class="rounded-md border border-dashed border-border px-3 py-3 text-sm">
|
|
368
|
+
<p class="font-medium text-foreground">No repositories available</p>
|
|
369
|
+
{#if allowRepositoryCreation}
|
|
370
|
+
<label class="mt-2 flex cursor-pointer items-start gap-2">
|
|
371
|
+
<input
|
|
372
|
+
type="checkbox"
|
|
373
|
+
bind:checked={createRepositoryIfMissing}
|
|
374
|
+
class="mt-0.5 accent-primary"
|
|
375
|
+
/>
|
|
376
|
+
<span>
|
|
377
|
+
Create a repository when this assignment starts
|
|
378
|
+
<span class="mt-1 block text-xs text-muted-foreground">
|
|
379
|
+
Your project visibility and repository plan still apply.
|
|
380
|
+
</span>
|
|
381
|
+
</span>
|
|
382
|
+
</label>
|
|
383
|
+
{:else}
|
|
384
|
+
<p class="mt-1 text-xs text-muted-foreground">
|
|
385
|
+
Use an ephemeral workspace for scratch work, or create a git.vibes.dev repository
|
|
386
|
+
before starting a repository-backed session.
|
|
387
|
+
</p>
|
|
388
|
+
{/if}
|
|
389
|
+
{#if repositoryCreateHref}
|
|
390
|
+
<Button
|
|
391
|
+
href={repositoryCreateHref}
|
|
392
|
+
size="sm"
|
|
393
|
+
variant="outline"
|
|
394
|
+
class="mt-3 h-8 gap-1 text-xs"
|
|
395
|
+
>
|
|
396
|
+
<Icon icon="lucide:external-link" class="size-3.5" aria-hidden="true" />
|
|
397
|
+
{repositoryCreateLabel}
|
|
398
|
+
</Button>
|
|
399
|
+
{/if}
|
|
400
|
+
</div>
|
|
401
|
+
{/if}
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<!-- Branch/ref applies only to a repository that already exists. -->
|
|
405
|
+
{#if repositories.length > 0}
|
|
406
|
+
<div class="space-y-2">
|
|
407
|
+
<Label for="ref-input">
|
|
408
|
+
Branch or commit
|
|
409
|
+
<span class="ml-1 text-xs text-muted-foreground">(optional)</span>
|
|
410
|
+
</Label>
|
|
411
|
+
<Input
|
|
412
|
+
id="ref-input"
|
|
413
|
+
type="text"
|
|
414
|
+
placeholder={selectedRepo?.defaultBranch ?? 'main'}
|
|
415
|
+
bind:value={checkoutRef}
|
|
416
|
+
/>
|
|
417
|
+
</div>
|
|
418
|
+
{/if}
|
|
419
|
+
{/if}
|
|
420
|
+
|
|
421
|
+
{#if createError}
|
|
422
|
+
<!-- role="alert": a failed create used to render silently to assistive tech. -->
|
|
423
|
+
<div
|
|
424
|
+
role="alert"
|
|
425
|
+
class="flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive"
|
|
426
|
+
>
|
|
427
|
+
<Icon icon="lucide:circle-x" class="mt-0.5 size-4 shrink-0" aria-hidden="true" />
|
|
428
|
+
{createError}
|
|
429
|
+
</div>
|
|
430
|
+
{/if}
|
|
431
|
+
|
|
432
|
+
{#if submitBlocker && !creating}
|
|
433
|
+
<!--
|
|
434
|
+
Say WHY "Start session" is unavailable. A disabled button with no
|
|
435
|
+
explanation is a dead end, and the worst case — an empty
|
|
436
|
+
repository list — is exactly when the user most needs to be told
|
|
437
|
+
where to go instead.
|
|
438
|
+
-->
|
|
439
|
+
<p
|
|
440
|
+
id="create-session-blocker"
|
|
441
|
+
class="flex items-start gap-2 text-sm text-muted-foreground"
|
|
442
|
+
role="status"
|
|
443
|
+
aria-live="polite"
|
|
444
|
+
>
|
|
445
|
+
<Icon icon="lucide:info" class="mt-0.5 size-4 shrink-0" aria-hidden="true" />
|
|
446
|
+
{submitBlocker.message}
|
|
447
|
+
</p>
|
|
448
|
+
{/if}
|
|
449
|
+
|
|
450
|
+
<Dialog.Footer>
|
|
451
|
+
<Button type="button" variant="outline" onclick={onclose} disabled={creating}>
|
|
452
|
+
Cancel
|
|
453
|
+
</Button>
|
|
454
|
+
<Button
|
|
455
|
+
type="submit"
|
|
456
|
+
disabled={creating || !canStartSession}
|
|
457
|
+
aria-describedby={submitBlocker && !creating ? 'create-session-blocker' : undefined}
|
|
458
|
+
>
|
|
459
|
+
{#if creating}
|
|
460
|
+
<Icon icon="lucide:loader-2" class="mr-2 size-4 animate-spin" aria-hidden="true" />
|
|
461
|
+
Starting…
|
|
462
|
+
{:else}
|
|
463
|
+
<Icon icon="lucide:play" class="mr-2 size-4" aria-hidden="true" />
|
|
464
|
+
{submitLabel}
|
|
465
|
+
{/if}
|
|
466
|
+
</Button>
|
|
467
|
+
</Dialog.Footer>
|
|
468
|
+
</form>
|
|
469
|
+
</Dialog.Content>
|
|
470
|
+
</Dialog.Root>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CodeSessionCreateInput, CodeSessionSeed } from '@vibesdotdev/code-sessions';
|
|
2
|
+
export type HostTargetOption = 'local' | 'fleet' | 'cloud';
|
|
3
|
+
export interface FleetHostOption {
|
|
4
|
+
hostId: string;
|
|
5
|
+
name: string;
|
|
6
|
+
online: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface CreateSessionDialogProps {
|
|
9
|
+
open?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Task this session is being started for, when the user got here from one.
|
|
12
|
+
*
|
|
13
|
+
* Reuses the platform's existing `CodeSessionSeed` contract rather than a
|
|
14
|
+
* web-only shape: the work-items TUI already creates sessions with
|
|
15
|
+
* `{ source: 'work-item', title, ref }`, the platform threads it into the
|
|
16
|
+
* sandbox as `VIBES_SEED_*`, and the session record persists it verbatim.
|
|
17
|
+
* A second convention here would mean a session started from the web could
|
|
18
|
+
* not be traced back the way one started from the TUI can.
|
|
19
|
+
*/
|
|
20
|
+
seed?: CodeSessionSeed;
|
|
21
|
+
/** Called when the user submits the form; parent handles the actual create call. */
|
|
22
|
+
oncreate?: (input: CodeSessionCreateInput) => void | Promise<void>;
|
|
23
|
+
/** Called when the dialog is closed. */
|
|
24
|
+
onclose?: () => void;
|
|
25
|
+
/** Repository options to populate the picker. */
|
|
26
|
+
repositories?: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
defaultBranch?: string | null;
|
|
30
|
+
description?: string | null;
|
|
31
|
+
group?: string;
|
|
32
|
+
}>;
|
|
33
|
+
/** Whether the create action is in progress. */
|
|
34
|
+
creating?: boolean;
|
|
35
|
+
/** Configuration-only embeddings may continue to a separate dispatch step. */
|
|
36
|
+
submitLabel?: string;
|
|
37
|
+
/** Error from a failed create attempt. */
|
|
38
|
+
createError?: string | null;
|
|
39
|
+
/** Optional URL for creating a repository in the owning forge surface. */
|
|
40
|
+
repositoryCreateHref?: string;
|
|
41
|
+
repositoryCreateLabel?: string;
|
|
42
|
+
/** Hide scratch mode when the caller requires a repository-backed outcome. */
|
|
43
|
+
allowEphemeral?: boolean;
|
|
44
|
+
/** Let the user explicitly request repository provisioning at the caller's submit boundary. */
|
|
45
|
+
allowRepositoryCreation?: boolean;
|
|
46
|
+
/** Execution targets the embedding surface can actually provision. */
|
|
47
|
+
allowedHostTargets?: HostTargetOption[];
|
|
48
|
+
/**
|
|
49
|
+
* Fleet machines available for code execution (labeled sandbox:code-execution).
|
|
50
|
+
* When empty the "Fleet host" radio is disabled and an onboarding hint is shown.
|
|
51
|
+
*/
|
|
52
|
+
fleetHosts?: FleetHostOption[];
|
|
53
|
+
}
|
|
54
|
+
declare const CreateSessionDialog: import("svelte").Component<CreateSessionDialogProps, {}, "">;
|
|
55
|
+
type CreateSessionDialog = ReturnType<typeof CreateSessionDialog>;
|
|
56
|
+
export default CreateSessionDialog;
|
|
57
|
+
//# sourceMappingURL=CreateSessionDialog.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateSessionDialog.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/CreateSessionDialog.svelte.ts"],"names":[],"mappings":"AAGC,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE1F,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,iDAAiD;IACjD,YAAY,CAAC,EAAE,KAAK,CAAC;QACpB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0EAA0E;IAC1E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+FAA+F;IAC/F,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;CAC/B;AAqWF,QAAA,MAAM,mBAAmB,8DAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
|