@spotpatch/dev-server 0.9.0 → 0.10.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/README.md +2 -0
- package/dist/index.cjs +135 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -8
- package/dist/index.d.ts +29 -8
- package/dist/index.js +136 -14
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ Applications should install a framework adapter such as [`@spotpatch/vite`](http
|
|
|
8
8
|
|
|
9
9
|
The service is development-only. Production adapters must omit it together with all private endpoints and source-registration state.
|
|
10
10
|
|
|
11
|
+
`createDevelopmentSession` owns the common AI, read-only Ask, external handoff and middleware lifecycle for Vite, Next and Astro. Frameworks inject bridge/validation/source-projection factories, retain their own transports, and call the idempotent `close()` on shutdown. Astro source imports remain subject to the same registry, path and read-budget authorization.
|
|
12
|
+
|
|
11
13
|
Requires Node.js `>=20.19.0`.
|
|
12
14
|
|
|
13
15
|
## 简体中文
|
package/dist/index.cjs
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
createAgentJobManager: () => createAgentJobManager,
|
|
40
40
|
createConfiguredKeyAskExecutors: () => createConfiguredKeyAskExecutors,
|
|
41
41
|
createContextualAskManager: () => createContextualAskManager,
|
|
42
|
+
createDevelopmentSession: () => createDevelopmentSession,
|
|
42
43
|
createExternalHandoffService: () => createExternalHandoffService,
|
|
43
44
|
createIntegrationFileChange: () => createIntegrationFileChange,
|
|
44
45
|
createRuntimeAiConfig: () => createRuntimeAiConfig,
|
|
@@ -1087,6 +1088,7 @@ var import_node_path = __toESM(require("path"), 1);
|
|
|
1087
1088
|
var import_shared5 = require("@spotpatch/shared");
|
|
1088
1089
|
var import_external_agent_node2 = require("@spotpatch/shared/external-agent-node");
|
|
1089
1090
|
async function syncDirectory(directory) {
|
|
1091
|
+
if (process.platform === "win32") return;
|
|
1090
1092
|
const handle = await (0, import_promises.open)(directory, "r");
|
|
1091
1093
|
try {
|
|
1092
1094
|
await handle.sync();
|
|
@@ -1136,14 +1138,11 @@ async function publishExternalHandoffDescriptor(options) {
|
|
|
1136
1138
|
} finally {
|
|
1137
1139
|
await handle.close();
|
|
1138
1140
|
}
|
|
1141
|
+
await (0, import_external_agent_node2.initializePrivateExternalHandoffFile)(temporary);
|
|
1139
1142
|
await (0, import_promises.rename)(temporary, destination);
|
|
1140
1143
|
temporaryExists = false;
|
|
1141
1144
|
published = true;
|
|
1142
|
-
const status = await (0,
|
|
1143
|
-
const uid = process.getuid?.();
|
|
1144
|
-
if (!status.isFile() || status.isSymbolicLink() || uid === void 0 || status.uid !== uid || (status.mode & 63) !== 0) {
|
|
1145
|
-
throw new Error("SpotPatch external Agent descriptor is not private.");
|
|
1146
|
-
}
|
|
1145
|
+
const status = await (0, import_external_agent_node2.assertPrivateExternalHandoffPath)(destination, "file");
|
|
1147
1146
|
descriptorIdentity = Object.freeze({ device: status.dev, inode: status.ino });
|
|
1148
1147
|
await syncDirectory(directory);
|
|
1149
1148
|
} catch (error) {
|
|
@@ -2457,14 +2456,14 @@ async function resolveProjectOptions(input) {
|
|
|
2457
2456
|
"SpotPatch trustedFastMode cannot be combined with applyMode auto."
|
|
2458
2457
|
);
|
|
2459
2458
|
}
|
|
2460
|
-
const checks = await resolveProjectValidationChecks({
|
|
2459
|
+
const checks = await (input.resolveValidationChecks ?? resolveProjectValidationChecks)({
|
|
2461
2460
|
appRoot: input.appRoot,
|
|
2462
2461
|
checks: resolved.ai.execution.checks,
|
|
2463
2462
|
timeoutMs: resolved.ai.execution.limits.checkTimeoutMs
|
|
2464
2463
|
});
|
|
2465
2464
|
if (!Object.values(checks).some((check) => check.required)) {
|
|
2466
2465
|
throw new RangeError(
|
|
2467
|
-
"SpotPatch trustedFastMode requires a configured required check or
|
|
2466
|
+
"SpotPatch trustedFastMode requires a configured required check or an available framework-compatible local project check."
|
|
2468
2467
|
);
|
|
2469
2468
|
}
|
|
2470
2469
|
const ai = Object.freeze({
|
|
@@ -2578,6 +2577,7 @@ var import_node_path5 = __toESM(require("path"), 1);
|
|
|
2578
2577
|
var import_shared10 = require("@spotpatch/shared");
|
|
2579
2578
|
var import_oxc_parser = require("oxc-parser");
|
|
2580
2579
|
var SOURCE_EXTENSIONS = Object.freeze([
|
|
2580
|
+
".astro",
|
|
2581
2581
|
".ts",
|
|
2582
2582
|
".tsx",
|
|
2583
2583
|
".js",
|
|
@@ -2886,7 +2886,8 @@ async function captureAskReadSnapshot(options) {
|
|
|
2886
2886
|
content: read.content
|
|
2887
2887
|
});
|
|
2888
2888
|
captured.push(entry);
|
|
2889
|
-
|
|
2889
|
+
const specifiers = options.resolveSourceImports?.(candidate.absolutePath, read.content) ?? localSpecifiers(candidate.absolutePath, read.content);
|
|
2890
|
+
for (const specifier of specifiers.filter((value) => value.startsWith("."))) {
|
|
2890
2891
|
options.signal?.throwIfAborted();
|
|
2891
2892
|
for (const importPath of importCandidates(candidate.absolutePath, specifier)) {
|
|
2892
2893
|
if (capturedPaths.has(importPath)) continue;
|
|
@@ -3022,6 +3023,7 @@ function fingerprint(request) {
|
|
|
3022
3023
|
JSON.stringify({
|
|
3023
3024
|
envelope: request.envelope,
|
|
3024
3025
|
executorId: request.executorId,
|
|
3026
|
+
model: request.model,
|
|
3025
3027
|
providerDataConsent: request.providerDataConsent
|
|
3026
3028
|
})
|
|
3027
3029
|
).digest("hex");
|
|
@@ -3312,7 +3314,8 @@ function createContextualAskManager(options) {
|
|
|
3312
3314
|
registry: options.registry,
|
|
3313
3315
|
selection: job.request.envelope.selection,
|
|
3314
3316
|
signal: job.controller.signal,
|
|
3315
|
-
createHandleId: dependencies.createId
|
|
3317
|
+
createHandleId: dependencies.createId,
|
|
3318
|
+
...options.resolveSourceImports === void 0 ? {} : { resolveSourceImports: options.resolveSourceImports }
|
|
3316
3319
|
});
|
|
3317
3320
|
if (job.controller.signal.aborted) {
|
|
3318
3321
|
captured.dispose();
|
|
@@ -3326,6 +3329,7 @@ function createContextualAskManager(options) {
|
|
|
3326
3329
|
job.executor.execute(
|
|
3327
3330
|
{
|
|
3328
3331
|
jobId: job.id,
|
|
3332
|
+
...job.request.model === void 0 ? {} : { model: job.request.model },
|
|
3329
3333
|
envelope: job.request.envelope,
|
|
3330
3334
|
grant: captured.grant,
|
|
3331
3335
|
snapshot: instrumentSnapshot(job, captured.snapshot, () => {
|
|
@@ -3445,6 +3449,9 @@ function createContextualAskManager(options) {
|
|
|
3445
3449
|
)
|
|
3446
3450
|
);
|
|
3447
3451
|
if (isClosed()) throw new ContextualAskError("ASK_DISABLED");
|
|
3452
|
+
if (request.model !== void 0 && !capability.models?.includes(request.model)) {
|
|
3453
|
+
throw new ContextualAskError("ASK_EXECUTOR_UNAVAILABLE");
|
|
3454
|
+
}
|
|
3448
3455
|
if (capability.executorId !== executor.executorId || capability.state !== "ready" || !capability.readOnlyProven) {
|
|
3449
3456
|
throw new ContextualAskError("ASK_EXECUTOR_UNAVAILABLE");
|
|
3450
3457
|
}
|
|
@@ -3461,7 +3468,11 @@ function createContextualAskManager(options) {
|
|
|
3461
3468
|
createdAt,
|
|
3462
3469
|
events: [],
|
|
3463
3470
|
executor,
|
|
3464
|
-
executorCapability: capability
|
|
3471
|
+
executorCapability: request.model === void 0 ? capability : {
|
|
3472
|
+
...capability,
|
|
3473
|
+
requestedModelLabel: request.model,
|
|
3474
|
+
effectiveModelLabel: request.model
|
|
3475
|
+
},
|
|
3465
3476
|
fingerprint: requestFingerprint,
|
|
3466
3477
|
id: dependencies.createId(),
|
|
3467
3478
|
lease,
|
|
@@ -4070,6 +4081,7 @@ function nearbyContext(options) {
|
|
|
4070
4081
|
});
|
|
4071
4082
|
}
|
|
4072
4083
|
function extractCodeContext(options) {
|
|
4084
|
+
if (options.language === "astro") return nearbyContext(options);
|
|
4073
4085
|
const component = findComponentSpan(options);
|
|
4074
4086
|
if (component !== void 0) {
|
|
4075
4087
|
const range = componentRange(options.source, component);
|
|
@@ -4093,7 +4105,7 @@ function extractCodeContext(options) {
|
|
|
4093
4105
|
var import_promises5 = require("fs/promises");
|
|
4094
4106
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
4095
4107
|
var import_shared13 = require("@spotpatch/shared");
|
|
4096
|
-
var ALLOWED_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx"]);
|
|
4108
|
+
var ALLOWED_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx", ".astro"]);
|
|
4097
4109
|
function isMissingFileError(error) {
|
|
4098
4110
|
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
4099
4111
|
}
|
|
@@ -4179,7 +4191,7 @@ async function readSourceContext(options) {
|
|
|
4179
4191
|
source,
|
|
4180
4192
|
sourcePath,
|
|
4181
4193
|
relativePath: toDisplayPath(await (0, import_promises6.realpath)(options.root), sourcePath),
|
|
4182
|
-
language: extension === ".tsx" ? "tsx" : "jsx",
|
|
4194
|
+
language: extension === ".astro" ? "astro" : extension === ".tsx" ? "tsx" : "jsx",
|
|
4183
4195
|
line: options.request.line,
|
|
4184
4196
|
column: options.request.column,
|
|
4185
4197
|
maxLines: Math.min(options.request.maxLines, options.maxLines),
|
|
@@ -4209,7 +4221,7 @@ function compactSourceRef(source) {
|
|
|
4209
4221
|
});
|
|
4210
4222
|
}
|
|
4211
4223
|
async function authorizeSourceRef(source, registry, root) {
|
|
4212
|
-
const markerOrigin = source.origin === "jsx-host" || source.origin === "dom-ancestor";
|
|
4224
|
+
const markerOrigin = source.origin === "jsx-host" || source.origin === "astro-host" || source.origin === "dom-ancestor";
|
|
4213
4225
|
if (markerOrigin && (source.fileId === void 0 || source.line === void 0 || source.column === void 0)) {
|
|
4214
4226
|
throw new import_shared15.SpotPatchError(import_shared15.ERROR_CODES.INVALID_REQUEST);
|
|
4215
4227
|
}
|
|
@@ -4894,7 +4906,8 @@ function createDataFlowAnalyzer(options) {
|
|
|
4894
4906
|
root: options.root,
|
|
4895
4907
|
registryEpoch: options.session.id,
|
|
4896
4908
|
registerSource: (absolutePath) => options.registry.register(absolutePath),
|
|
4897
|
-
limits: options.options.dataFlow.limits
|
|
4909
|
+
limits: options.options.dataFlow.limits,
|
|
4910
|
+
...options.projectDataFlowSource === void 0 ? {} : { projectSource: options.projectDataFlowSource }
|
|
4898
4911
|
});
|
|
4899
4912
|
}
|
|
4900
4913
|
async function analyzeTarget(request, analyzer, options) {
|
|
@@ -5849,6 +5862,113 @@ function parseSerializedSpotPatchOptions(value) {
|
|
|
5849
5862
|
});
|
|
5850
5863
|
}
|
|
5851
5864
|
}
|
|
5865
|
+
|
|
5866
|
+
// src/development-session.ts
|
|
5867
|
+
async function createDevelopmentSession(input) {
|
|
5868
|
+
const cleanups = [
|
|
5869
|
+
() => {
|
|
5870
|
+
input.registry.clear();
|
|
5871
|
+
}
|
|
5872
|
+
];
|
|
5873
|
+
let closing;
|
|
5874
|
+
const close = () => closing ??= (async () => {
|
|
5875
|
+
const errors = [];
|
|
5876
|
+
for (const cleanup of cleanups.splice(0).reverse()) {
|
|
5877
|
+
try {
|
|
5878
|
+
await cleanup();
|
|
5879
|
+
} catch (error) {
|
|
5880
|
+
errors.push(error);
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
if (errors.length > 0)
|
|
5884
|
+
throw new AggregateError(errors, "SpotPatch service cleanup failed.");
|
|
5885
|
+
})();
|
|
5886
|
+
const warn = (message) => {
|
|
5887
|
+
input.logger?.warn(`[spotpatch:${input.framework}] ${message}`);
|
|
5888
|
+
};
|
|
5889
|
+
try {
|
|
5890
|
+
const { options, root, registry } = input;
|
|
5891
|
+
const coordinator = createWorkspaceActivityCoordinator();
|
|
5892
|
+
const agentManager = options.ai === false ? void 0 : createAgentJobManager({
|
|
5893
|
+
ai: options.ai,
|
|
5894
|
+
environment: input.environment,
|
|
5895
|
+
root: input.executionRoot ?? root,
|
|
5896
|
+
coordinator
|
|
5897
|
+
});
|
|
5898
|
+
if (agentManager !== void 0) cleanups.push(() => agentManager.close());
|
|
5899
|
+
const preference = options.contextualAsk.defaultExecutor === void 0 ? {} : { defaultExecutor: options.contextualAsk.defaultExecutor };
|
|
5900
|
+
const contextualAskManager = !options.contextualAsk.enabled ? void 0 : createContextualAskManager({
|
|
5901
|
+
coordinator,
|
|
5902
|
+
enabled: true,
|
|
5903
|
+
registry,
|
|
5904
|
+
root,
|
|
5905
|
+
...input.resolveSourceImports === void 0 ? {} : { resolveSourceImports: input.resolveSourceImports },
|
|
5906
|
+
executors: composeContextualAskExecutors({
|
|
5907
|
+
configuredKey: options.ai === false ? [] : createConfiguredKeyAskExecutors({
|
|
5908
|
+
ai: options.ai,
|
|
5909
|
+
environment: input.environment,
|
|
5910
|
+
...preference
|
|
5911
|
+
}),
|
|
5912
|
+
managedCodex: input.createManagedAskExecutor(),
|
|
5913
|
+
...preference
|
|
5914
|
+
})
|
|
5915
|
+
});
|
|
5916
|
+
if (contextualAskManager !== void 0)
|
|
5917
|
+
cleanups.push(() => contextualAskManager.close());
|
|
5918
|
+
const externalHandoffService = !options.externalAgent.enabled ? void 0 : createExternalHandoffService({
|
|
5919
|
+
framework: input.framework,
|
|
5920
|
+
root,
|
|
5921
|
+
sessionId: input.session.id
|
|
5922
|
+
});
|
|
5923
|
+
let externalAgentControl;
|
|
5924
|
+
if (externalHandoffService !== void 0) {
|
|
5925
|
+
cleanups.push(() => externalHandoffService.close());
|
|
5926
|
+
try {
|
|
5927
|
+
await externalHandoffService.start();
|
|
5928
|
+
} catch {
|
|
5929
|
+
warn("External Agent handoff is unavailable; core tools remain active.");
|
|
5930
|
+
}
|
|
5931
|
+
if (externalHandoffService.capability().brokerReady) {
|
|
5932
|
+
try {
|
|
5933
|
+
const validation = await (input.resolveValidation?.() ?? resolveManagedExecutionValidation({ ai: options.ai, appRoot: root }));
|
|
5934
|
+
const control = await input.createExternalAgentControl(validation);
|
|
5935
|
+
externalAgentControl = control;
|
|
5936
|
+
cleanups.push(() => control.dispose());
|
|
5937
|
+
} catch {
|
|
5938
|
+
warn("Managed Agent control is unavailable; Inbox remains active.");
|
|
5939
|
+
}
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
const middleware = createSpotPatchMiddleware({
|
|
5943
|
+
options,
|
|
5944
|
+
root,
|
|
5945
|
+
registry,
|
|
5946
|
+
session: input.session,
|
|
5947
|
+
...input.projectDataFlowSource === void 0 ? {} : { projectDataFlowSource: input.projectDataFlowSource },
|
|
5948
|
+
...input.bootstrap === void 0 ? {} : { bootstrap: input.bootstrap },
|
|
5949
|
+
...input.logger === void 0 ? {} : { logger: input.logger },
|
|
5950
|
+
...input.editorLauncher === void 0 ? {} : { editorLauncher: input.editorLauncher },
|
|
5951
|
+
...agentManager === void 0 ? {} : { agentManager },
|
|
5952
|
+
...contextualAskManager === void 0 ? {} : { contextualAskManager },
|
|
5953
|
+
...externalHandoffService === void 0 ? {} : { externalHandoffService },
|
|
5954
|
+
...externalAgentControl === void 0 ? {} : { externalAgentControl }
|
|
5955
|
+
});
|
|
5956
|
+
cleanups.push(() => {
|
|
5957
|
+
middleware.dispose();
|
|
5958
|
+
});
|
|
5959
|
+
return Object.freeze({ middleware, close });
|
|
5960
|
+
} catch (error) {
|
|
5961
|
+
try {
|
|
5962
|
+
await close();
|
|
5963
|
+
} catch (cleanupError) {
|
|
5964
|
+
throw new AggregateError(
|
|
5965
|
+
[error, cleanupError],
|
|
5966
|
+
"SpotPatch service startup and cleanup failed."
|
|
5967
|
+
);
|
|
5968
|
+
}
|
|
5969
|
+
throw error;
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5852
5972
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5853
5973
|
0 && (module.exports = {
|
|
5854
5974
|
ContextualAskError,
|
|
@@ -5860,6 +5980,7 @@ function parseSerializedSpotPatchOptions(value) {
|
|
|
5860
5980
|
createAgentJobManager,
|
|
5861
5981
|
createConfiguredKeyAskExecutors,
|
|
5862
5982
|
createContextualAskManager,
|
|
5983
|
+
createDevelopmentSession,
|
|
5863
5984
|
createExternalHandoffService,
|
|
5864
5985
|
createIntegrationFileChange,
|
|
5865
5986
|
createRuntimeAiConfig,
|