@omercnet/paseo-gas-city 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/bun.lock +1217 -0
- package/client/city-operations.tsx +1505 -0
- package/client/contribute.tsx +96 -0
- package/client/dispatch-intent.ts +53 -0
- package/client/factory-panel.tsx +373 -0
- package/client/gas-city-surface.tsx +343 -0
- package/client/settings-screen.tsx +286 -0
- package/client/view-model.ts +318 -0
- package/docs/images/paseo-gas-city-compact-overview.webp +0 -0
- package/docs/images/paseo-gas-city-dispatch-confirmation.webp +0 -0
- package/docs/images/paseo-gas-city-wide-events.webp +0 -0
- package/docs/images/paseo-gas-city-wide-overview.webp +0 -0
- package/icon.svg +5 -0
- package/index.client.tsx +1 -0
- package/index.server.ts +42 -0
- package/package.json +73 -0
- package/paseo-plugin.json +4 -0
- package/server/gas-city-client.ts +668 -0
- package/server/handlers.ts +790 -0
- package/server/workspace-mapping.ts +170 -0
- package/shared/index.ts +4 -0
- package/shared/limits.ts +28 -0
- package/shared/rpc.ts +99 -0
- package/shared/schemas.ts +430 -0
- package/shared/settings.ts +89 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
import type { WorkspaceMappingOverride, WorkspaceRigMapping } from "../shared";
|
|
4
|
+
import { GAS_CITY_LIMITS, WorkspaceRigMappingSchema } from "../shared";
|
|
5
|
+
import type { UpstreamCity, UpstreamRig } from "./gas-city-client";
|
|
6
|
+
|
|
7
|
+
export interface WorkspaceMappingInput {
|
|
8
|
+
workspaceId: string;
|
|
9
|
+
workspacePath: string | null;
|
|
10
|
+
cities: readonly UpstreamCity[];
|
|
11
|
+
rigsByCity: ReadonlyMap<string, readonly UpstreamRig[]>;
|
|
12
|
+
overrides: readonly WorkspaceMappingOverride[];
|
|
13
|
+
canonicalizePath?: (path: string) => Promise<string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function canonicalPath(path: string): Promise<string> {
|
|
17
|
+
const absolute = resolve(path);
|
|
18
|
+
try {
|
|
19
|
+
return await realpath(absolute);
|
|
20
|
+
} catch {
|
|
21
|
+
return absolute;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isAncestorPath(ancestor: string, child: string): boolean {
|
|
26
|
+
const delta = relative(ancestor, child);
|
|
27
|
+
return delta === "" || (!delta.startsWith(`..${sep}`) && delta !== ".." && !isAbsolute(delta));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function diagnostic(code: string, message: string, retryable: boolean) {
|
|
31
|
+
return { code, message, retryable };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function mapWorkspaceToRig(
|
|
35
|
+
input: WorkspaceMappingInput,
|
|
36
|
+
): Promise<WorkspaceRigMapping> {
|
|
37
|
+
const canonicalize = input.canonicalizePath ?? canonicalPath;
|
|
38
|
+
const explicit = input.overrides.find(({ workspaceId }) => workspaceId === input.workspaceId);
|
|
39
|
+
if (explicit) {
|
|
40
|
+
const city = input.cities.find(({ name }) => name === explicit.cityName);
|
|
41
|
+
const rig = input.rigsByCity
|
|
42
|
+
.get(explicit.cityName)
|
|
43
|
+
?.find(({ name }) => name === explicit.rigName);
|
|
44
|
+
if (!city || !rig) {
|
|
45
|
+
return WorkspaceRigMappingSchema.parse({
|
|
46
|
+
state: "unavailable",
|
|
47
|
+
workspaceId: input.workspaceId,
|
|
48
|
+
workspacePath: input.workspacePath,
|
|
49
|
+
cityName: explicit.cityName,
|
|
50
|
+
rigName: explicit.rigName,
|
|
51
|
+
rigPath: null,
|
|
52
|
+
source: "explicit",
|
|
53
|
+
candidates: [],
|
|
54
|
+
diagnostics: [
|
|
55
|
+
diagnostic(
|
|
56
|
+
"mapping-target-unavailable",
|
|
57
|
+
"The explicitly configured Gas City rig is unavailable.",
|
|
58
|
+
true,
|
|
59
|
+
),
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return WorkspaceRigMappingSchema.parse({
|
|
64
|
+
state: "mapped",
|
|
65
|
+
workspaceId: input.workspaceId,
|
|
66
|
+
workspacePath: input.workspacePath,
|
|
67
|
+
cityName: city.name,
|
|
68
|
+
rigName: rig.name,
|
|
69
|
+
rigPath: rig.path,
|
|
70
|
+
source: "explicit",
|
|
71
|
+
candidates: [{ cityName: city.name, rigName: rig.name, rigPath: rig.path }],
|
|
72
|
+
diagnostics: [],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!input.workspacePath) {
|
|
77
|
+
return WorkspaceRigMappingSchema.parse({
|
|
78
|
+
state: "unmapped",
|
|
79
|
+
workspaceId: input.workspaceId,
|
|
80
|
+
workspacePath: null,
|
|
81
|
+
cityName: null,
|
|
82
|
+
rigName: null,
|
|
83
|
+
rigPath: null,
|
|
84
|
+
source: null,
|
|
85
|
+
candidates: [],
|
|
86
|
+
diagnostics: [
|
|
87
|
+
diagnostic("workspace-path-unavailable", "The Paseo workspace has no local path.", false),
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const workspacePath = await canonicalize(input.workspacePath);
|
|
93
|
+
const matches: Array<{
|
|
94
|
+
cityName: string;
|
|
95
|
+
rigName: string;
|
|
96
|
+
rigPath: string;
|
|
97
|
+
canonicalRigPath: string;
|
|
98
|
+
}> = [];
|
|
99
|
+
for (const city of input.cities) {
|
|
100
|
+
for (const rig of input.rigsByCity.get(city.name) ?? []) {
|
|
101
|
+
const rigPath = await canonicalize(rig.path);
|
|
102
|
+
if (isAncestorPath(rigPath, workspacePath)) {
|
|
103
|
+
matches.push({
|
|
104
|
+
cityName: city.name,
|
|
105
|
+
rigName: rig.name,
|
|
106
|
+
rigPath: rig.path,
|
|
107
|
+
canonicalRigPath: rigPath,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
matches.sort(
|
|
113
|
+
(left, right) =>
|
|
114
|
+
right.canonicalRigPath.length - left.canonicalRigPath.length ||
|
|
115
|
+
left.cityName.localeCompare(right.cityName) ||
|
|
116
|
+
left.rigName.localeCompare(right.rigName),
|
|
117
|
+
);
|
|
118
|
+
const candidates = matches.slice(0, GAS_CITY_LIMITS.mappingCandidates).map((match) => ({
|
|
119
|
+
cityName: match.cityName,
|
|
120
|
+
rigName: match.rigName,
|
|
121
|
+
rigPath: match.rigPath,
|
|
122
|
+
}));
|
|
123
|
+
const winner = matches[0];
|
|
124
|
+
if (!winner) {
|
|
125
|
+
return WorkspaceRigMappingSchema.parse({
|
|
126
|
+
state: "unmapped",
|
|
127
|
+
workspaceId: input.workspaceId,
|
|
128
|
+
workspacePath,
|
|
129
|
+
cityName: null,
|
|
130
|
+
rigName: null,
|
|
131
|
+
rigPath: null,
|
|
132
|
+
source: null,
|
|
133
|
+
candidates: [],
|
|
134
|
+
diagnostics: [],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const equallySpecific = matches.filter(
|
|
138
|
+
({ canonicalRigPath }) => canonicalRigPath.length === winner.canonicalRigPath.length,
|
|
139
|
+
);
|
|
140
|
+
if (equallySpecific.length > 1) {
|
|
141
|
+
return WorkspaceRigMappingSchema.parse({
|
|
142
|
+
state: "ambiguous",
|
|
143
|
+
workspaceId: input.workspaceId,
|
|
144
|
+
workspacePath,
|
|
145
|
+
cityName: null,
|
|
146
|
+
rigName: null,
|
|
147
|
+
rigPath: null,
|
|
148
|
+
source: null,
|
|
149
|
+
candidates,
|
|
150
|
+
diagnostics: [
|
|
151
|
+
diagnostic(
|
|
152
|
+
"ambiguous-rig-mapping",
|
|
153
|
+
"Multiple equally specific Gas City rigs contain this workspace.",
|
|
154
|
+
false,
|
|
155
|
+
),
|
|
156
|
+
],
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return WorkspaceRigMappingSchema.parse({
|
|
160
|
+
state: "mapped",
|
|
161
|
+
workspaceId: input.workspaceId,
|
|
162
|
+
workspacePath,
|
|
163
|
+
cityName: winner.cityName,
|
|
164
|
+
rigName: winner.rigName,
|
|
165
|
+
rigPath: winner.rigPath,
|
|
166
|
+
source: "longest-ancestor",
|
|
167
|
+
candidates,
|
|
168
|
+
diagnostics: [],
|
|
169
|
+
});
|
|
170
|
+
}
|
package/shared/index.ts
ADDED
package/shared/limits.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const GAS_CITY_LIMITS = {
|
|
2
|
+
endpointUrl: 2_048,
|
|
3
|
+
identifier: 256,
|
|
4
|
+
name: 256,
|
|
5
|
+
path: 4_096,
|
|
6
|
+
title: 4_096,
|
|
7
|
+
status: 128,
|
|
8
|
+
message: 16_384,
|
|
9
|
+
prompt: 131_072,
|
|
10
|
+
errorMessage: 2_048,
|
|
11
|
+
eventType: 256,
|
|
12
|
+
timestamp: 64,
|
|
13
|
+
cursor: 4_096,
|
|
14
|
+
metadataKey: 128,
|
|
15
|
+
metadataValue: 4_096,
|
|
16
|
+
metadataEntries: 64,
|
|
17
|
+
cities: 128,
|
|
18
|
+
rigs: 512,
|
|
19
|
+
sessions: 1_000,
|
|
20
|
+
convoys: 500,
|
|
21
|
+
workItems: 500,
|
|
22
|
+
events: 500,
|
|
23
|
+
attentionItems: 500,
|
|
24
|
+
diagnostics: 32,
|
|
25
|
+
mappingCandidates: 32,
|
|
26
|
+
providers: 128,
|
|
27
|
+
warnings: 32,
|
|
28
|
+
} as const;
|
package/shared/rpc.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { defineRpc } from "@getpaseo/plugin";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { GAS_CITY_LIMITS } from "./limits";
|
|
4
|
+
import {
|
|
5
|
+
AttentionListSchema,
|
|
6
|
+
CityRigSnapshotSchema,
|
|
7
|
+
ConvoyListSchema,
|
|
8
|
+
DispatchRequestSchema,
|
|
9
|
+
DispatchResultSchema,
|
|
10
|
+
EventListSchema,
|
|
11
|
+
identifierSchema,
|
|
12
|
+
nameSchema,
|
|
13
|
+
SessionActionRequestSchema,
|
|
14
|
+
SessionActionResultSchema,
|
|
15
|
+
SessionListSchema,
|
|
16
|
+
SupervisorDiscoverySchema,
|
|
17
|
+
WorkListSchema,
|
|
18
|
+
WorkspaceRigMappingSchema,
|
|
19
|
+
} from "./schemas";
|
|
20
|
+
import { GasCityRpcSettingsSchema } from "./settings";
|
|
21
|
+
|
|
22
|
+
const settingsInput = { settings: GasCityRpcSettingsSchema };
|
|
23
|
+
const noInput = z.object(settingsInput).strict();
|
|
24
|
+
const cityRigInput = z
|
|
25
|
+
.object({
|
|
26
|
+
...settingsInput,
|
|
27
|
+
cityName: nameSchema,
|
|
28
|
+
rigName: nameSchema.nullable(),
|
|
29
|
+
})
|
|
30
|
+
.strict();
|
|
31
|
+
|
|
32
|
+
export const discoverSupervisor = defineRpc({
|
|
33
|
+
name: "gas-city.discover-supervisor",
|
|
34
|
+
input: noInput,
|
|
35
|
+
output: SupervisorDiscoverySchema,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const resolveWorkspaceRig = defineRpc({
|
|
39
|
+
name: "gas-city.resolve-workspace-rig",
|
|
40
|
+
input: z.object({ ...settingsInput, workspaceId: identifierSchema }).strict(),
|
|
41
|
+
output: WorkspaceRigMappingSchema,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const getCityRigSnapshot = defineRpc({
|
|
45
|
+
name: "gas-city.get-city-rig-snapshot",
|
|
46
|
+
input: cityRigInput,
|
|
47
|
+
output: CityRigSnapshotSchema,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const listSessions = defineRpc({
|
|
51
|
+
name: "gas-city.list-sessions",
|
|
52
|
+
input: cityRigInput,
|
|
53
|
+
output: SessionListSchema,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const listConvoys = defineRpc({
|
|
57
|
+
name: "gas-city.list-convoys",
|
|
58
|
+
input: cityRigInput,
|
|
59
|
+
output: ConvoyListSchema,
|
|
60
|
+
});
|
|
61
|
+
export const listWork = defineRpc({
|
|
62
|
+
name: "gas-city.list-work",
|
|
63
|
+
input: cityRigInput,
|
|
64
|
+
output: WorkListSchema,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const listEvents = defineRpc({
|
|
68
|
+
name: "gas-city.list-events",
|
|
69
|
+
input: z.discriminatedUnion("scope", [
|
|
70
|
+
z.object({ ...settingsInput, scope: z.literal("supervisor") }).strict(),
|
|
71
|
+
z
|
|
72
|
+
.object({
|
|
73
|
+
...settingsInput,
|
|
74
|
+
scope: z.literal("city"),
|
|
75
|
+
cityName: nameSchema,
|
|
76
|
+
cursor: z.string().max(GAS_CITY_LIMITS.cursor).nullable(),
|
|
77
|
+
})
|
|
78
|
+
.strict(),
|
|
79
|
+
]),
|
|
80
|
+
output: EventListSchema,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export const listAttention = defineRpc({
|
|
84
|
+
name: "gas-city.list-attention",
|
|
85
|
+
input: cityRigInput,
|
|
86
|
+
output: AttentionListSchema,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const dispatchWork = defineRpc({
|
|
90
|
+
name: "gas-city.dispatch-work",
|
|
91
|
+
input: z.object({ ...settingsInput, request: DispatchRequestSchema }).strict(),
|
|
92
|
+
output: DispatchResultSchema,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export const performSessionAction = defineRpc({
|
|
96
|
+
name: "gas-city.perform-session-action",
|
|
97
|
+
input: z.object({ ...settingsInput, request: SessionActionRequestSchema }).strict(),
|
|
98
|
+
output: SessionActionResultSchema,
|
|
99
|
+
});
|