@schoolai/shipyard 3.3.0 → 3.3.1-rc.20260425.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/dist/{chunk-OHIIVBEZ.js → chunk-5OAMOVYJ.js} +2 -2
- package/dist/{chunk-UWBX6UGC.js → chunk-QUDDKCMA.js} +3 -3
- package/dist/chunk-QUDDKCMA.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/{login-N43V35KE.js → login-HJFQQD4A.js} +3 -3
- package/dist/{serve-QLYUK5XN.js → serve-2J25TKJX.js} +746 -285
- package/dist/{serve-QLYUK5XN.js.map → serve-2J25TKJX.js.map} +1 -1
- package/dist/{start-NVYMND3X.js → start-6AKQQHRF.js} +4 -4
- package/package.json +5 -2
- package/dist/chunk-UWBX6UGC.js.map +0 -1
- /package/dist/{chunk-OHIIVBEZ.js.map → chunk-5OAMOVYJ.js.map} +0 -0
- /package/dist/{login-N43V35KE.js.map → login-HJFQQD4A.js.map} +0 -0
- /package/dist/{start-NVYMND3X.js.map → start-6AKQQHRF.js.map} +0 -0
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
VaultKeyPutRequestSchema,
|
|
54
54
|
VaultKeyPutResponseSchema,
|
|
55
55
|
classifyClaudeCodeCompatibility
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-QUDDKCMA.js";
|
|
57
57
|
import "./chunk-EHQITHQX.js";
|
|
58
58
|
import {
|
|
59
59
|
loadAuthToken
|
|
@@ -14592,7 +14592,7 @@ function parseWorktreeListOutput(output, repoPath) {
|
|
|
14592
14592
|
// src/shared/capabilities/index.ts
|
|
14593
14593
|
import { readFile as readFile3 } from "fs/promises";
|
|
14594
14594
|
import { homedir as homedir2 } from "os";
|
|
14595
|
-
import { join as
|
|
14595
|
+
import { join as join6 } from "path";
|
|
14596
14596
|
|
|
14597
14597
|
// src/shared/capabilities/agents.ts
|
|
14598
14598
|
async function detectAgentProviders() {
|
|
@@ -14850,7 +14850,6 @@ async function detectMarketplacePlugins() {
|
|
|
14850
14850
|
// src/shared/capabilities/effort-probe.ts
|
|
14851
14851
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
14852
14852
|
import { createRequire } from "module";
|
|
14853
|
-
import { dirname as dirname3, join as join6 } from "path";
|
|
14854
14853
|
|
|
14855
14854
|
// ../../packages/loro-schema/dist/index.js
|
|
14856
14855
|
import { LoroDoc as LoroDoc4 } from "loro-crdt";
|
|
@@ -27700,6 +27699,58 @@ var TaskColorSchema = external_exports.enum(TASK_COLORS);
|
|
|
27700
27699
|
var WorktreeScriptSchema = external_exports.object({
|
|
27701
27700
|
script: external_exports.string()
|
|
27702
27701
|
});
|
|
27702
|
+
var LinearSortFieldSchema = external_exports.enum([
|
|
27703
|
+
"priority",
|
|
27704
|
+
"updated",
|
|
27705
|
+
"created",
|
|
27706
|
+
"status",
|
|
27707
|
+
"assignee",
|
|
27708
|
+
"estimate",
|
|
27709
|
+
"title"
|
|
27710
|
+
]);
|
|
27711
|
+
var LinearSortDirectionSchema = external_exports.enum(["asc", "desc"]);
|
|
27712
|
+
var LinearGroupBySchema = external_exports.enum([
|
|
27713
|
+
"none",
|
|
27714
|
+
"status",
|
|
27715
|
+
"priority",
|
|
27716
|
+
"assignee",
|
|
27717
|
+
"team",
|
|
27718
|
+
"label",
|
|
27719
|
+
"parent",
|
|
27720
|
+
"project"
|
|
27721
|
+
]);
|
|
27722
|
+
var LinearViewPreferencesSchema = external_exports.object({
|
|
27723
|
+
filters: external_exports.object({
|
|
27724
|
+
status: external_exports.array(external_exports.string()),
|
|
27725
|
+
priority: external_exports.array(external_exports.number()),
|
|
27726
|
+
labels: external_exports.array(external_exports.string()),
|
|
27727
|
+
assignee: external_exports.array(external_exports.string()),
|
|
27728
|
+
team: external_exports.array(external_exports.string()),
|
|
27729
|
+
project: external_exports.array(external_exports.string()),
|
|
27730
|
+
hasEstimate: external_exports.boolean().nullable(),
|
|
27731
|
+
createdAfter: external_exports.string().nullable(),
|
|
27732
|
+
createdBefore: external_exports.string().nullable()
|
|
27733
|
+
}),
|
|
27734
|
+
sortBy: LinearSortFieldSchema,
|
|
27735
|
+
sortDirection: LinearSortDirectionSchema,
|
|
27736
|
+
displayProperties: external_exports.object({
|
|
27737
|
+
identifier: external_exports.boolean(),
|
|
27738
|
+
status: external_exports.boolean(),
|
|
27739
|
+
assignee: external_exports.boolean(),
|
|
27740
|
+
priority: external_exports.boolean(),
|
|
27741
|
+
estimate: external_exports.boolean(),
|
|
27742
|
+
labels: external_exports.boolean(),
|
|
27743
|
+
created: external_exports.boolean(),
|
|
27744
|
+
subIssues: external_exports.boolean(),
|
|
27745
|
+
team: external_exports.boolean(),
|
|
27746
|
+
updated: external_exports.boolean()
|
|
27747
|
+
}),
|
|
27748
|
+
groupBy: LinearGroupBySchema,
|
|
27749
|
+
subGroupBy: LinearGroupBySchema,
|
|
27750
|
+
showSubIssues: external_exports.boolean(),
|
|
27751
|
+
showEmptyGroups: external_exports.boolean(),
|
|
27752
|
+
searchQuery: external_exports.string()
|
|
27753
|
+
});
|
|
27703
27754
|
var UserSettingsRecordSchema = external_exports.object({
|
|
27704
27755
|
composerModel: external_exports.string().nullable(),
|
|
27705
27756
|
composerReasoning: ReasoningEffortSchema.nullable(),
|
|
@@ -27713,9 +27764,12 @@ var UserSettingsRecordSchema = external_exports.object({
|
|
|
27713
27764
|
builtInTemplateOrder: external_exports.array(external_exports.string()).default([]),
|
|
27714
27765
|
taskColors: external_exports.record(external_exports.string(), TaskColorSchema).default({}),
|
|
27715
27766
|
favoriteTasks: external_exports.array(external_exports.string()).default([]),
|
|
27716
|
-
collapsedProjects: external_exports.array(external_exports.string()).default([])
|
|
27767
|
+
collapsedProjects: external_exports.array(external_exports.string()).default([]),
|
|
27768
|
+
linearViewPreferences: LinearViewPreferencesSchema.nullable().default(null),
|
|
27769
|
+
linearScope: external_exports.enum(["my-issues", "team-issues"]).nullable().default(null),
|
|
27770
|
+
linearSelectedTeamId: external_exports.string().nullable().default(null)
|
|
27717
27771
|
});
|
|
27718
|
-
var USER_SETTINGS_STORE_VERSION =
|
|
27772
|
+
var USER_SETTINGS_STORE_VERSION = 4;
|
|
27719
27773
|
var UserSettingsStoreSchema = external_exports.object({
|
|
27720
27774
|
schemaVersion: external_exports.number(),
|
|
27721
27775
|
settings: UserSettingsRecordSchema
|
|
@@ -27746,7 +27800,10 @@ var DEFAULT_USER_SETTINGS = {
|
|
|
27746
27800
|
builtInTemplateOrder: [],
|
|
27747
27801
|
taskColors: {},
|
|
27748
27802
|
favoriteTasks: [],
|
|
27749
|
-
collapsedProjects: []
|
|
27803
|
+
collapsedProjects: [],
|
|
27804
|
+
linearViewPreferences: null,
|
|
27805
|
+
linearScope: null,
|
|
27806
|
+
linearSelectedTeamId: null
|
|
27750
27807
|
};
|
|
27751
27808
|
function migrateUserSettingsStore(raw) {
|
|
27752
27809
|
if (typeof raw !== "object" || raw === null) {
|
|
@@ -28056,8 +28113,7 @@ var RequestPublishShape = external_exports.object({
|
|
|
28056
28113
|
external_exports.object({
|
|
28057
28114
|
kind: external_exports.literal("previewPort"),
|
|
28058
28115
|
previewPort: external_exports.number().int().positive().max(65535),
|
|
28059
|
-
canvasElementId: external_exports.string().optional()
|
|
28060
|
-
projectRoot: external_exports.string().optional()
|
|
28116
|
+
canvasElementId: external_exports.string().optional()
|
|
28061
28117
|
})
|
|
28062
28118
|
]),
|
|
28063
28119
|
ttl: external_exports.enum(["24h", "7d", "30d"]),
|
|
@@ -28106,6 +28162,20 @@ var PublishedArtifactsStateShape = external_exports.object({
|
|
|
28106
28162
|
taskId: external_exports.string(),
|
|
28107
28163
|
entries: external_exports.array(PublishedArtifactRecordShape)
|
|
28108
28164
|
});
|
|
28165
|
+
var PreviewElementStateShape = external_exports.object({
|
|
28166
|
+
elementId: external_exports.string().min(1),
|
|
28167
|
+
ownerUserId: external_exports.string(),
|
|
28168
|
+
port: external_exports.number().int().positive().optional(),
|
|
28169
|
+
url: external_exports.string().optional(),
|
|
28170
|
+
proxyPort: external_exports.number().int().positive().optional(),
|
|
28171
|
+
initialPath: external_exports.string().optional(),
|
|
28172
|
+
projectRoot: external_exports.string().optional()
|
|
28173
|
+
});
|
|
28174
|
+
var PreviewElementsStateShape = external_exports.object({
|
|
28175
|
+
type: external_exports.literal("preview_elements_state"),
|
|
28176
|
+
taskId: external_exports.string(),
|
|
28177
|
+
entries: external_exports.array(PreviewElementStateShape)
|
|
28178
|
+
});
|
|
28109
28179
|
var BrowserToDaemonControlMessageSchema = external_exports.discriminatedUnion("type", [
|
|
28110
28180
|
external_exports.object({
|
|
28111
28181
|
type: external_exports.literal("permission_response"),
|
|
@@ -28451,6 +28521,7 @@ var RateLimitInfoSchema = external_exports.object({
|
|
|
28451
28521
|
"org_service_level_disabled",
|
|
28452
28522
|
"org_service_zero_credit_limit",
|
|
28453
28523
|
"no_limits_configured",
|
|
28524
|
+
"fetch_error",
|
|
28454
28525
|
"unknown"
|
|
28455
28526
|
]).optional(),
|
|
28456
28527
|
isUsingOverage: external_exports.boolean().optional(),
|
|
@@ -28938,7 +29009,8 @@ var DaemonToBrowserControlMessageSchema = external_exports.discriminatedUnion("t
|
|
|
28938
29009
|
}),
|
|
28939
29010
|
PublishProgressShape,
|
|
28940
29011
|
PublishResultShape,
|
|
28941
|
-
PublishedArtifactsStateShape
|
|
29012
|
+
PublishedArtifactsStateShape,
|
|
29013
|
+
PreviewElementsStateShape
|
|
28942
29014
|
]);
|
|
28943
29015
|
var TASK_MESSAGES_PREFIX = "task-messages:";
|
|
28944
29016
|
var DAEMON_CONTROL_LABEL = "daemon-control";
|
|
@@ -29319,8 +29391,7 @@ var PublishTargetSchema = external_exports.discriminatedUnion("kind", [
|
|
|
29319
29391
|
external_exports.object({
|
|
29320
29392
|
kind: external_exports.literal("previewPort"),
|
|
29321
29393
|
previewPort: external_exports.number().int().positive().max(65535),
|
|
29322
|
-
canvasElementId: external_exports.string().optional()
|
|
29323
|
-
projectRoot: external_exports.string().optional()
|
|
29394
|
+
canvasElementId: external_exports.string().optional()
|
|
29324
29395
|
})
|
|
29325
29396
|
]);
|
|
29326
29397
|
var PublishInputSchema = external_exports.object({
|
|
@@ -30319,21 +30390,27 @@ function isReasoningEffort(value) {
|
|
|
30319
30390
|
}
|
|
30320
30391
|
function probeCli() {
|
|
30321
30392
|
try {
|
|
30322
|
-
const
|
|
30323
|
-
|
|
30324
|
-
const cliPath = join6(dirname3(sdkMain), "cli.js");
|
|
30325
|
-
if (!existsSync2(cliPath)) {
|
|
30393
|
+
const binaryPath = resolveBundledClaudeBinary();
|
|
30394
|
+
if (!binaryPath || !existsSync2(binaryPath)) {
|
|
30326
30395
|
logger.warn(
|
|
30327
|
-
{
|
|
30328
|
-
"bundled Claude Code
|
|
30396
|
+
{ binaryPath },
|
|
30397
|
+
"bundled Claude Code binary not found; using conservative effort set"
|
|
30329
30398
|
);
|
|
30330
30399
|
return { supported: CONSERVATIVE_FALLBACK, source: "fallback" };
|
|
30331
30400
|
}
|
|
30332
|
-
const
|
|
30401
|
+
const slice2 = readEffortDescriptionFromBinary(binaryPath);
|
|
30402
|
+
if (slice2 === null) {
|
|
30403
|
+
logger.warn(
|
|
30404
|
+
{ binaryPath },
|
|
30405
|
+
"effort description not found in bundled binary; using conservative effort set"
|
|
30406
|
+
);
|
|
30407
|
+
return { supported: CONSERVATIVE_FALLBACK, source: "fallback" };
|
|
30408
|
+
}
|
|
30409
|
+
const parsed = parseEffortsFromCliText(slice2);
|
|
30333
30410
|
if (!parsed) {
|
|
30334
30411
|
logger.warn(
|
|
30335
|
-
{
|
|
30336
|
-
"could not parse effort tokens from bundled
|
|
30412
|
+
{ binaryPath },
|
|
30413
|
+
"could not parse effort tokens from bundled binary; using conservative effort set"
|
|
30337
30414
|
);
|
|
30338
30415
|
return { supported: CONSERVATIVE_FALLBACK, source: "fallback" };
|
|
30339
30416
|
}
|
|
@@ -30343,6 +30420,32 @@ function probeCli() {
|
|
|
30343
30420
|
return { supported: CONSERVATIVE_FALLBACK, source: "fallback" };
|
|
30344
30421
|
}
|
|
30345
30422
|
}
|
|
30423
|
+
function resolveBundledClaudeBinary() {
|
|
30424
|
+
const req = createRequire(import.meta.url);
|
|
30425
|
+
const sdkMain = req.resolve("@anthropic-ai/claude-agent-sdk");
|
|
30426
|
+
const sdkReq = createRequire(sdkMain);
|
|
30427
|
+
const ext2 = process.platform === "win32" ? ".exe" : "";
|
|
30428
|
+
const candidates = process.platform === "linux" ? [
|
|
30429
|
+
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}-musl`,
|
|
30430
|
+
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}`
|
|
30431
|
+
] : [`@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`];
|
|
30432
|
+
for (const pkg of candidates) {
|
|
30433
|
+
try {
|
|
30434
|
+
return sdkReq.resolve(`${pkg}/claude${ext2}`);
|
|
30435
|
+
} catch (err) {
|
|
30436
|
+
logger.debug({ pkg, err }, "platform claude binary candidate not resolvable");
|
|
30437
|
+
}
|
|
30438
|
+
}
|
|
30439
|
+
return null;
|
|
30440
|
+
}
|
|
30441
|
+
function readEffortDescriptionFromBinary(binaryPath) {
|
|
30442
|
+
const buf = readFileSync(binaryPath);
|
|
30443
|
+
const needle = Buffer.from("Effort level for the current session");
|
|
30444
|
+
const idx = buf.indexOf(needle);
|
|
30445
|
+
if (idx === -1) return null;
|
|
30446
|
+
const end = Math.min(idx + 200, buf.length);
|
|
30447
|
+
return buf.toString("utf-8", idx, end);
|
|
30448
|
+
}
|
|
30346
30449
|
|
|
30347
30450
|
// src/shared/capabilities/models.ts
|
|
30348
30451
|
async function detectModels() {
|
|
@@ -31139,7 +31242,7 @@ var AutoModeConfigSchema = external_exports.object({
|
|
|
31139
31242
|
}).passthrough();
|
|
31140
31243
|
async function detectAutoModeEnabled() {
|
|
31141
31244
|
try {
|
|
31142
|
-
const claudeConfig = await readFile3(
|
|
31245
|
+
const claudeConfig = await readFile3(join6(homedir2(), ".claude.json"), "utf-8");
|
|
31143
31246
|
const result = AutoModeConfigSchema.safeParse(JSON.parse(claudeConfig));
|
|
31144
31247
|
return result.success && result.data.cachedGrowthBookFeatures.tengu_auto_mode_config.enabled === "enabled";
|
|
31145
31248
|
} catch {
|
|
@@ -31208,7 +31311,7 @@ async function detectCapabilitiesWithInitialRetry(tokenStore, methodHint, lastKn
|
|
|
31208
31311
|
|
|
31209
31312
|
// src/shared/file-storage-adapter.ts
|
|
31210
31313
|
import { access, mkdir as mkdir2, readdir as readdir3, readFile as readFile4, rename as rename2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
31211
|
-
import { dirname as
|
|
31314
|
+
import { dirname as dirname3, join as join7, sep } from "path";
|
|
31212
31315
|
var FileStorageAdapter = class _FileStorageAdapter extends StorageAdapter {
|
|
31213
31316
|
#dataDir;
|
|
31214
31317
|
constructor(dataDir) {
|
|
@@ -31226,7 +31329,7 @@ var FileStorageAdapter = class _FileStorageAdapter extends StorageAdapter {
|
|
|
31226
31329
|
}
|
|
31227
31330
|
async save(key, data) {
|
|
31228
31331
|
const filePath = this.#keyToPath(key);
|
|
31229
|
-
const dir =
|
|
31332
|
+
const dir = dirname3(filePath);
|
|
31230
31333
|
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
31231
31334
|
const tmpPath = `${filePath}.tmp`;
|
|
31232
31335
|
await writeFile2(tmpPath, data, { mode: 384 });
|
|
@@ -31268,7 +31371,7 @@ var FileStorageAdapter = class _FileStorageAdapter extends StorageAdapter {
|
|
|
31268
31371
|
* docId is the first key segment (e.g. "canvas:<taskId>:<epoch>").
|
|
31269
31372
|
*/
|
|
31270
31373
|
async hasDoc(docId) {
|
|
31271
|
-
const dirPath =
|
|
31374
|
+
const dirPath = join7(this.#dataDir, encodeURIComponent(docId));
|
|
31272
31375
|
try {
|
|
31273
31376
|
await access(dirPath);
|
|
31274
31377
|
return true;
|
|
@@ -31282,7 +31385,7 @@ var FileStorageAdapter = class _FileStorageAdapter extends StorageAdapter {
|
|
|
31282
31385
|
}
|
|
31283
31386
|
#keyToPath(key) {
|
|
31284
31387
|
const sanitized = key.map((part) => encodeURIComponent(part));
|
|
31285
|
-
return
|
|
31388
|
+
return join7(this.#dataDir, ...sanitized);
|
|
31286
31389
|
}
|
|
31287
31390
|
#pathToKey(filePath) {
|
|
31288
31391
|
const relative5 = filePath.slice(this.#dataDir.length + 1);
|
|
@@ -31300,7 +31403,7 @@ var FileStorageAdapter = class _FileStorageAdapter extends StorageAdapter {
|
|
|
31300
31403
|
return;
|
|
31301
31404
|
}
|
|
31302
31405
|
for (const entry of entries) {
|
|
31303
|
-
const fullPath =
|
|
31406
|
+
const fullPath = join7(dir, entry.name);
|
|
31304
31407
|
if (entry.isDirectory()) {
|
|
31305
31408
|
await this.#walkDir(fullPath, keyPrefix, results);
|
|
31306
31409
|
} else if (entry.isFile() && !entry.name.endsWith(".tmp")) {
|
|
@@ -31478,7 +31581,7 @@ var KeepAwakeManager = class {
|
|
|
31478
31581
|
// src/shared/mcp/token-store.ts
|
|
31479
31582
|
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
31480
31583
|
import { readFile as readFile5, rename as rename3, stat as stat3, writeFile as writeFile3 } from "fs/promises";
|
|
31481
|
-
import { join as
|
|
31584
|
+
import { join as join8 } from "path";
|
|
31482
31585
|
var TOKEN_FILE = "mcp-tokens.json";
|
|
31483
31586
|
var REFRESH_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
31484
31587
|
var MCPTokenStore = class {
|
|
@@ -31496,7 +31599,7 @@ var MCPTokenStore = class {
|
|
|
31496
31599
|
this.#onChange = cb;
|
|
31497
31600
|
}
|
|
31498
31601
|
#filePath() {
|
|
31499
|
-
return
|
|
31602
|
+
return join8(this.#shipyardHome, TOKEN_FILE);
|
|
31500
31603
|
}
|
|
31501
31604
|
/** Read mtime from disk. Returns -1 if file does not exist. */
|
|
31502
31605
|
#readMtimeMs() {
|
|
@@ -31608,7 +31711,7 @@ var MCPTokenStore = class {
|
|
|
31608
31711
|
// src/services/bootstrap/lifecycle.ts
|
|
31609
31712
|
import { unlinkSync } from "fs";
|
|
31610
31713
|
import { readFile as readFile6, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
|
|
31611
|
-
import { join as
|
|
31714
|
+
import { join as join9 } from "path";
|
|
31612
31715
|
|
|
31613
31716
|
// src/services/bootstrap/classify-uncaught-error.ts
|
|
31614
31717
|
function classifyUncaughtError(error2) {
|
|
@@ -31659,6 +31762,15 @@ function getLoroCrdtVersion() {
|
|
|
31659
31762
|
}
|
|
31660
31763
|
|
|
31661
31764
|
// src/services/bootstrap/lifecycle.ts
|
|
31765
|
+
function extractWasmPanicContext(error2) {
|
|
31766
|
+
const stack = error2 instanceof Error && typeof error2.stack === "string" ? error2.stack : "";
|
|
31767
|
+
const frames = stack.split("\n").map((f2) => f2.trim());
|
|
31768
|
+
const wasmHashMatch = stack.match(/wasm:\/\/wasm\/([0-9a-f]+):/);
|
|
31769
|
+
const wasmModuleHash = wasmHashMatch?.[1] ?? null;
|
|
31770
|
+
const jsFrames = frames.filter((f2) => f2.startsWith("at ") && !f2.includes("wasm://wasm/"));
|
|
31771
|
+
const jsLeafFrame = jsFrames[0]?.replace(/^at /, "") ?? null;
|
|
31772
|
+
return { jsLeafFrame, jsStack: jsFrames.slice(0, 8), wasmModuleHash };
|
|
31773
|
+
}
|
|
31662
31774
|
function isProcessAlive(pid) {
|
|
31663
31775
|
try {
|
|
31664
31776
|
process.kill(pid, 0);
|
|
@@ -31742,7 +31854,7 @@ var LifecycleManager = class {
|
|
|
31742
31854
|
this.#removePidFileSync();
|
|
31743
31855
|
}
|
|
31744
31856
|
async acquirePidFile(shipyardHome) {
|
|
31745
|
-
const pidFilePath =
|
|
31857
|
+
const pidFilePath = join9(shipyardHome, "daemon.pid");
|
|
31746
31858
|
try {
|
|
31747
31859
|
const existing = await readFile6(pidFilePath, "utf-8");
|
|
31748
31860
|
const raw = existing.trim();
|
|
@@ -31820,6 +31932,7 @@ var LifecycleManager = class {
|
|
|
31820
31932
|
} catch (diagErr) {
|
|
31821
31933
|
this.#safeLog("warn", { err: diagErr, origin }, "WASM panic diagnostics collection failed");
|
|
31822
31934
|
}
|
|
31935
|
+
const { jsLeafFrame, jsStack, wasmModuleHash } = extractWasmPanicContext(error2);
|
|
31823
31936
|
this.#safeLog(
|
|
31824
31937
|
"warn",
|
|
31825
31938
|
{
|
|
@@ -31827,7 +31940,23 @@ var LifecycleManager = class {
|
|
|
31827
31940
|
origin,
|
|
31828
31941
|
loroCrdtVersion: getLoroCrdtVersion(),
|
|
31829
31942
|
docs: diagnostics,
|
|
31830
|
-
docCount: diagnostics.length
|
|
31943
|
+
docCount: diagnostics.length,
|
|
31944
|
+
/**
|
|
31945
|
+
* JS-side context for panic class disambiguation (issue #2354).
|
|
31946
|
+
* jsLeafFrame: first non-wasm JS frame — identifies the code path
|
|
31947
|
+
* (e.g. LoroMapFinalization → finalization class,
|
|
31948
|
+
* subscribeLocalUpdates → reentry class, etc.)
|
|
31949
|
+
* jsStack: compact non-wasm call stack for triage without a full trace.
|
|
31950
|
+
* wasmModuleHash: discriminates Loro version (00c34872 = 1.11.1 stock,
|
|
31951
|
+
* 00bf5d86 = Shipyard fork 7dfda879, 00c501de = 1.10.8).
|
|
31952
|
+
*
|
|
31953
|
+
* TODO (#2354): add std::panic::set_hook in the vendored Loro wasm
|
|
31954
|
+
* build so this log also carries the Rust-level panic message and
|
|
31955
|
+
* file:line, making every panic class unambiguous.
|
|
31956
|
+
*/
|
|
31957
|
+
jsLeafFrame,
|
|
31958
|
+
jsStack,
|
|
31959
|
+
wasmModuleHash
|
|
31831
31960
|
},
|
|
31832
31961
|
"WASM runtime panic \u2014 absorbed"
|
|
31833
31962
|
);
|
|
@@ -31939,10 +32068,10 @@ var LifecycleManager = class {
|
|
|
31939
32068
|
// src/services/bootstrap/load-or-create-peer-id.ts
|
|
31940
32069
|
import { randomUUID } from "crypto";
|
|
31941
32070
|
import { mkdir as mkdir3, readFile as readFile7, rename as rename4, writeFile as writeFile5 } from "fs/promises";
|
|
31942
|
-
import { dirname as
|
|
32071
|
+
import { dirname as dirname4, join as join10 } from "path";
|
|
31943
32072
|
var PEER_ID_FILE = "daemon-peer-id";
|
|
31944
32073
|
async function loadOrCreateDaemonPeerId(shipyardDataDir) {
|
|
31945
|
-
const target =
|
|
32074
|
+
const target = join10(shipyardDataDir, PEER_ID_FILE);
|
|
31946
32075
|
const existing = await readExistingPeerId(target);
|
|
31947
32076
|
if (existing !== null) return existing;
|
|
31948
32077
|
const peerId = generatePeerId();
|
|
@@ -31967,7 +32096,7 @@ async function readExistingPeerId(target) {
|
|
|
31967
32096
|
return candidate;
|
|
31968
32097
|
}
|
|
31969
32098
|
async function writePeerIdAtomic(target, peerId) {
|
|
31970
|
-
await mkdir3(
|
|
32099
|
+
await mkdir3(dirname4(target), { recursive: true });
|
|
31971
32100
|
const tmp = `${target}.${randomUUID()}.tmp`;
|
|
31972
32101
|
await writeFile5(tmp, peerId, "utf-8");
|
|
31973
32102
|
await rename4(tmp, target);
|
|
@@ -31975,10 +32104,10 @@ async function writePeerIdAtomic(target, peerId) {
|
|
|
31975
32104
|
|
|
31976
32105
|
// src/services/bootstrap/pid-tracking.ts
|
|
31977
32106
|
import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
31978
|
-
import { join as
|
|
32107
|
+
import { join as join11 } from "path";
|
|
31979
32108
|
var PID_FILE = "children.pid";
|
|
31980
32109
|
function buildPidTracker(shipyardHome) {
|
|
31981
|
-
const filePath =
|
|
32110
|
+
const filePath = join11(shipyardHome, PID_FILE);
|
|
31982
32111
|
let queue = Promise.resolve();
|
|
31983
32112
|
function enqueue(fn) {
|
|
31984
32113
|
const next = queue.then(fn);
|
|
@@ -32061,7 +32190,7 @@ function buildReloadDocsFromStorage(repoRef, log) {
|
|
|
32061
32190
|
// src/services/bootstrap/signaling.ts
|
|
32062
32191
|
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
32063
32192
|
import { hostname } from "os";
|
|
32064
|
-
import { dirname as
|
|
32193
|
+
import { dirname as dirname5, join as join12 } from "path";
|
|
32065
32194
|
|
|
32066
32195
|
// ../../node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/index.js
|
|
32067
32196
|
import { webcrypto as crypto2 } from "crypto";
|
|
@@ -32094,7 +32223,7 @@ function nanoid(size2 = 21) {
|
|
|
32094
32223
|
}
|
|
32095
32224
|
|
|
32096
32225
|
// src/services/bootstrap/signaling.ts
|
|
32097
|
-
var DAEMON_NPM_VERSION = true ? "3.3.
|
|
32226
|
+
var DAEMON_NPM_VERSION = true ? "3.3.1" : "unknown";
|
|
32098
32227
|
function createDaemonSignaling(config2) {
|
|
32099
32228
|
const agentId = config2.agentId ?? nanoid();
|
|
32100
32229
|
function send(msg) {
|
|
@@ -32123,7 +32252,7 @@ function createDaemonSignaling(config2) {
|
|
|
32123
32252
|
};
|
|
32124
32253
|
}
|
|
32125
32254
|
function getOrCreateAgentId(shipyardHome) {
|
|
32126
|
-
const filePath =
|
|
32255
|
+
const filePath = join12(shipyardHome, "agent-id");
|
|
32127
32256
|
try {
|
|
32128
32257
|
const existing = readFileSync3(filePath, "utf-8").trim();
|
|
32129
32258
|
if (existing) return existing;
|
|
@@ -32131,7 +32260,7 @@ function getOrCreateAgentId(shipyardHome) {
|
|
|
32131
32260
|
}
|
|
32132
32261
|
const id = crypto.randomUUID();
|
|
32133
32262
|
try {
|
|
32134
|
-
mkdirSync(
|
|
32263
|
+
mkdirSync(dirname5(filePath), { recursive: true });
|
|
32135
32264
|
writeFileSync(filePath, id, { mode: 384 });
|
|
32136
32265
|
} catch {
|
|
32137
32266
|
}
|
|
@@ -32339,7 +32468,7 @@ function createPeerRoleRegistry() {
|
|
|
32339
32468
|
|
|
32340
32469
|
// src/services/epoch-pruning.ts
|
|
32341
32470
|
import { readdir as readdir4, rm } from "fs/promises";
|
|
32342
|
-
import { join as
|
|
32471
|
+
import { join as join13 } from "path";
|
|
32343
32472
|
var LEGACY_PREFIXES = [
|
|
32344
32473
|
"task-meta",
|
|
32345
32474
|
"task-conv",
|
|
@@ -32379,7 +32508,7 @@ async function pruneOldEpochData(dataDir, currentEpoch, log) {
|
|
|
32379
32508
|
}
|
|
32380
32509
|
if (!shouldPrune(decoded, currentEpoch)) continue;
|
|
32381
32510
|
removals.push(
|
|
32382
|
-
rm(
|
|
32511
|
+
rm(join13(dataDir, entry.name), { recursive: true }).then(() => {
|
|
32383
32512
|
pruned++;
|
|
32384
32513
|
}).catch((err) => {
|
|
32385
32514
|
log({
|
|
@@ -34003,6 +34132,7 @@ function createLocalDirectPeer(config2) {
|
|
|
34003
34132
|
},
|
|
34004
34133
|
"local-direct attachDataChannel failed"
|
|
34005
34134
|
);
|
|
34135
|
+
config2.onAttachDataChannelFailed?.();
|
|
34006
34136
|
channel.close();
|
|
34007
34137
|
}
|
|
34008
34138
|
return;
|
|
@@ -34385,7 +34515,7 @@ function onConnection(ws, deps, peers) {
|
|
|
34385
34515
|
|
|
34386
34516
|
// src/services/local-direct/local-direct-token.ts
|
|
34387
34517
|
import { chmod, mkdir as mkdir4, rename as rename5, rm as rm2, writeFile as writeFile7 } from "fs/promises";
|
|
34388
|
-
import { dirname as
|
|
34518
|
+
import { dirname as dirname6, join as join14 } from "path";
|
|
34389
34519
|
var ADVERTISEMENT_FILE = "local-direct.json";
|
|
34390
34520
|
var ADVERTISEMENT_MODE = 384;
|
|
34391
34521
|
var ADVERTISEMENT_DIR_MODE = 448;
|
|
@@ -34394,11 +34524,11 @@ function generateLocalDirectToken() {
|
|
|
34394
34524
|
return nanoid(TOKEN_LENGTH);
|
|
34395
34525
|
}
|
|
34396
34526
|
function advertisementPath(shipyardHome) {
|
|
34397
|
-
return
|
|
34527
|
+
return join14(shipyardHome, "data", ADVERTISEMENT_FILE);
|
|
34398
34528
|
}
|
|
34399
34529
|
async function writeAdvertisement(shipyardHome, ad) {
|
|
34400
34530
|
const target = advertisementPath(shipyardHome);
|
|
34401
|
-
const dir =
|
|
34531
|
+
const dir = dirname6(target);
|
|
34402
34532
|
await mkdir4(dir, { recursive: true, mode: ADVERTISEMENT_DIR_MODE });
|
|
34403
34533
|
try {
|
|
34404
34534
|
await chmod(dir, ADVERTISEMENT_DIR_MODE);
|
|
@@ -34426,14 +34556,39 @@ async function deleteAdvertisement(shipyardHome) {
|
|
|
34426
34556
|
}
|
|
34427
34557
|
|
|
34428
34558
|
// src/services/local-direct/local-direct-wiring.ts
|
|
34559
|
+
var CANARY_WINDOW_MS = 3e4;
|
|
34560
|
+
var CANARY_THRESHOLD = 5;
|
|
34561
|
+
var CANARY_EMIT_INTERVAL_MS = 3e4;
|
|
34562
|
+
function createAttachFailureCanary(log, logAdapter) {
|
|
34563
|
+
const failureTimes = [];
|
|
34564
|
+
let lastCanaryAt = 0;
|
|
34565
|
+
return function onAttachDataChannelFailed() {
|
|
34566
|
+
const now = Date.now();
|
|
34567
|
+
failureTimes.push(now);
|
|
34568
|
+
const windowStart = now - CANARY_WINDOW_MS;
|
|
34569
|
+
while (failureTimes.length > 0 && (failureTimes[0] ?? 0) < windowStart) {
|
|
34570
|
+
failureTimes.shift();
|
|
34571
|
+
}
|
|
34572
|
+
if (failureTimes.length >= CANARY_THRESHOLD && now - lastCanaryAt >= CANARY_EMIT_INTERVAL_MS) {
|
|
34573
|
+
lastCanaryAt = now;
|
|
34574
|
+
const rate = failureTimes.length;
|
|
34575
|
+
log.warn(
|
|
34576
|
+
{ event: "pre_storm_canary", failuresInWindow: rate, windowMs: CANARY_WINDOW_MS },
|
|
34577
|
+
"pre_storm_canary: local-direct attach failure rate elevated \u2014 potential storm incoming"
|
|
34578
|
+
);
|
|
34579
|
+
logAdapter({ event: "pre_storm_canary", failuresInWindow: rate, windowMs: CANARY_WINDOW_MS });
|
|
34580
|
+
}
|
|
34581
|
+
};
|
|
34582
|
+
}
|
|
34429
34583
|
async function setupLocalDirect(deps) {
|
|
34430
34584
|
const token = generateLocalDirectToken();
|
|
34585
|
+
const onAttachDataChannelFailed = createAttachFailureCanary(deps.log, deps.logAdapter);
|
|
34431
34586
|
const server = await createLocalDirectServer({
|
|
34432
34587
|
token,
|
|
34433
34588
|
webrtcAdapter: deps.webrtcAdapter,
|
|
34434
34589
|
log: deps.log,
|
|
34435
34590
|
logAdapter: deps.logAdapter,
|
|
34436
|
-
channelCallbacks: deps.channelCallbacks
|
|
34591
|
+
channelCallbacks: { ...deps.channelCallbacks, onAttachDataChannelFailed }
|
|
34437
34592
|
});
|
|
34438
34593
|
await writeAdvertisement(deps.shipyardHome, {
|
|
34439
34594
|
port: server.port,
|
|
@@ -34523,7 +34678,7 @@ function createMetricsCollector(workerUrl, authToken, telemetryEnabled) {
|
|
|
34523
34678
|
// src/services/plugins/plugin-file-watcher.ts
|
|
34524
34679
|
import { existsSync as existsSync3, watch } from "fs";
|
|
34525
34680
|
import { readdir as readdir5, readFile as readFile9, stat as stat4 } from "fs/promises";
|
|
34526
|
-
import { join as
|
|
34681
|
+
import { join as join15 } from "path";
|
|
34527
34682
|
import { pathToFileURL } from "url";
|
|
34528
34683
|
|
|
34529
34684
|
// src/shared/plugins/plugin-registry.ts
|
|
@@ -34624,7 +34779,7 @@ var PluginFileWatcher = class {
|
|
|
34624
34779
|
}
|
|
34625
34780
|
const loaded = [];
|
|
34626
34781
|
for (const entry of entries) {
|
|
34627
|
-
const pluginDir =
|
|
34782
|
+
const pluginDir = join15(dir, entry);
|
|
34628
34783
|
let stats;
|
|
34629
34784
|
try {
|
|
34630
34785
|
stats = await stat4(pluginDir);
|
|
@@ -34639,7 +34794,7 @@ var PluginFileWatcher = class {
|
|
|
34639
34794
|
this.#reconcile(loaded);
|
|
34640
34795
|
}
|
|
34641
34796
|
async #loadPlugin(id, pluginDir) {
|
|
34642
|
-
const manifestPath =
|
|
34797
|
+
const manifestPath = join15(pluginDir, "plugin.json");
|
|
34643
34798
|
let manifestRaw;
|
|
34644
34799
|
try {
|
|
34645
34800
|
manifestRaw = await readFile9(manifestPath, "utf-8");
|
|
@@ -34666,7 +34821,7 @@ var PluginFileWatcher = class {
|
|
|
34666
34821
|
}
|
|
34667
34822
|
const manifest = parsed.data;
|
|
34668
34823
|
let template = "";
|
|
34669
|
-
const templatePath =
|
|
34824
|
+
const templatePath = join15(pluginDir, "template.html");
|
|
34670
34825
|
try {
|
|
34671
34826
|
template = await readFile9(templatePath, "utf-8");
|
|
34672
34827
|
} catch {
|
|
@@ -34681,7 +34836,7 @@ var PluginFileWatcher = class {
|
|
|
34681
34836
|
};
|
|
34682
34837
|
}
|
|
34683
34838
|
async #loadAndRegisterHandler(id, pluginDir, title) {
|
|
34684
|
-
const handlerPath =
|
|
34839
|
+
const handlerPath = join15(pluginDir, "handler.mjs");
|
|
34685
34840
|
let handlerFn;
|
|
34686
34841
|
try {
|
|
34687
34842
|
const handlerStat = await stat4(handlerPath);
|
|
@@ -34856,7 +35011,7 @@ var LIST_MY_TEAMS_QUERY = `
|
|
|
34856
35011
|
}
|
|
34857
35012
|
`;
|
|
34858
35013
|
var LIST_TEAM_ISSUES_QUERY = `
|
|
34859
|
-
query ListTeamIssues($teamId:
|
|
35014
|
+
query ListTeamIssues($teamId: String!, $first: Int, $after: String) {
|
|
34860
35015
|
team(id: $teamId) {
|
|
34861
35016
|
issues(
|
|
34862
35017
|
first: $first
|
|
@@ -35348,7 +35503,7 @@ function handlePluginAuthRequest(pluginId, sendControl, deps, logAdapter) {
|
|
|
35348
35503
|
// src/services/port-detection.ts
|
|
35349
35504
|
import { execFile as execFile3 } from "child_process";
|
|
35350
35505
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
35351
|
-
import { dirname as
|
|
35506
|
+
import { dirname as dirname7, join as join16 } from "path";
|
|
35352
35507
|
import { promisify as promisify2 } from "util";
|
|
35353
35508
|
var execFileAsync = promisify2(execFile3);
|
|
35354
35509
|
var EXEC_TIMEOUT_MS2 = 5e3;
|
|
@@ -35463,7 +35618,7 @@ function hasStringName(val) {
|
|
|
35463
35618
|
function resolveProjectRoot(cwd) {
|
|
35464
35619
|
let dir = cwd;
|
|
35465
35620
|
while (true) {
|
|
35466
|
-
const candidate =
|
|
35621
|
+
const candidate = join16(dir, "package.json");
|
|
35467
35622
|
if (existsSync4(candidate)) {
|
|
35468
35623
|
let packageName;
|
|
35469
35624
|
try {
|
|
@@ -35476,7 +35631,7 @@ function resolveProjectRoot(cwd) {
|
|
|
35476
35631
|
}
|
|
35477
35632
|
return { projectRoot: dir, packageName };
|
|
35478
35633
|
}
|
|
35479
|
-
const parent =
|
|
35634
|
+
const parent = dirname7(dir);
|
|
35480
35635
|
if (parent === dir) {
|
|
35481
35636
|
return {};
|
|
35482
35637
|
}
|
|
@@ -35573,6 +35728,175 @@ async function getCwdByPid(pids) {
|
|
|
35573
35728
|
}
|
|
35574
35729
|
}
|
|
35575
35730
|
|
|
35731
|
+
// src/services/preview/preview-state-store.ts
|
|
35732
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
35733
|
+
import {
|
|
35734
|
+
mkdirSync as mkdirSync2,
|
|
35735
|
+
readdirSync,
|
|
35736
|
+
readFileSync as readFileSync5,
|
|
35737
|
+
renameSync,
|
|
35738
|
+
unlinkSync as unlinkSync2,
|
|
35739
|
+
writeFileSync as writeFileSync2
|
|
35740
|
+
} from "fs";
|
|
35741
|
+
import { join as join17 } from "path";
|
|
35742
|
+
function isEnoent2(err) {
|
|
35743
|
+
return err instanceof Error && Reflect.get(err, "code") === "ENOENT";
|
|
35744
|
+
}
|
|
35745
|
+
function atomicWriteSync(filePath, content) {
|
|
35746
|
+
const tmpPath = `${filePath}.${randomUUID2()}.tmp`;
|
|
35747
|
+
writeFileSync2(tmpPath, content, "utf-8");
|
|
35748
|
+
renameSync(tmpPath, filePath);
|
|
35749
|
+
}
|
|
35750
|
+
function recordFilePath(rootDir, taskId, elementId) {
|
|
35751
|
+
return join17(rootDir, taskId, `${elementId}.json`);
|
|
35752
|
+
}
|
|
35753
|
+
function parseRecord(filePath, logger2) {
|
|
35754
|
+
let raw;
|
|
35755
|
+
try {
|
|
35756
|
+
raw = readFileSync5(filePath, "utf-8");
|
|
35757
|
+
} catch {
|
|
35758
|
+
return null;
|
|
35759
|
+
}
|
|
35760
|
+
let obj;
|
|
35761
|
+
try {
|
|
35762
|
+
obj = JSON.parse(raw);
|
|
35763
|
+
} catch {
|
|
35764
|
+
logger2.warn({ filePath }, "preview_state_store: invalid JSON, skipping");
|
|
35765
|
+
return null;
|
|
35766
|
+
}
|
|
35767
|
+
const result = PreviewElementStateShape.safeParse(obj);
|
|
35768
|
+
if (!result.success) {
|
|
35769
|
+
logger2.warn(
|
|
35770
|
+
{ filePath, issues: result.error.issues },
|
|
35771
|
+
"preview_state_store: schema validation failed, skipping"
|
|
35772
|
+
);
|
|
35773
|
+
return null;
|
|
35774
|
+
}
|
|
35775
|
+
return result.data;
|
|
35776
|
+
}
|
|
35777
|
+
function createPreviewStateStore(opts) {
|
|
35778
|
+
const { rootDir, logger: logger2 } = opts;
|
|
35779
|
+
const registry = /* @__PURE__ */ new Map();
|
|
35780
|
+
let sendFn = null;
|
|
35781
|
+
mkdirSync2(rootDir, { recursive: true });
|
|
35782
|
+
function getTaskMap(taskId) {
|
|
35783
|
+
let taskMap = registry.get(taskId);
|
|
35784
|
+
if (!taskMap) {
|
|
35785
|
+
taskMap = /* @__PURE__ */ new Map();
|
|
35786
|
+
registry.set(taskId, taskMap);
|
|
35787
|
+
}
|
|
35788
|
+
return taskMap;
|
|
35789
|
+
}
|
|
35790
|
+
function scanTaskDir(taskId) {
|
|
35791
|
+
const taskPath = join17(rootDir, taskId);
|
|
35792
|
+
let files;
|
|
35793
|
+
try {
|
|
35794
|
+
files = readdirSync(taskPath);
|
|
35795
|
+
} catch {
|
|
35796
|
+
return;
|
|
35797
|
+
}
|
|
35798
|
+
for (const file of files) {
|
|
35799
|
+
if (!file.endsWith(".json")) continue;
|
|
35800
|
+
const record = parseRecord(join17(taskPath, file), logger2);
|
|
35801
|
+
if (record) getTaskMap(taskId).set(record.elementId, record);
|
|
35802
|
+
}
|
|
35803
|
+
}
|
|
35804
|
+
function scanRootDir() {
|
|
35805
|
+
let taskDirs;
|
|
35806
|
+
try {
|
|
35807
|
+
taskDirs = readdirSync(rootDir);
|
|
35808
|
+
} catch {
|
|
35809
|
+
return;
|
|
35810
|
+
}
|
|
35811
|
+
for (const taskId of taskDirs) {
|
|
35812
|
+
scanTaskDir(taskId);
|
|
35813
|
+
}
|
|
35814
|
+
}
|
|
35815
|
+
scanRootDir();
|
|
35816
|
+
function broadcastTask(taskId) {
|
|
35817
|
+
if (!sendFn) return;
|
|
35818
|
+
const taskMap = registry.get(taskId);
|
|
35819
|
+
const entries = taskMap ? [...taskMap.values()] : [];
|
|
35820
|
+
entries.sort((a, b2) => a.elementId < b2.elementId ? -1 : a.elementId > b2.elementId ? 1 : 0);
|
|
35821
|
+
sendFn({ type: "preview_elements_state", taskId, entries });
|
|
35822
|
+
}
|
|
35823
|
+
function writeToDisk(taskId, state) {
|
|
35824
|
+
const filePath = recordFilePath(rootDir, taskId, state.elementId);
|
|
35825
|
+
try {
|
|
35826
|
+
mkdirSync2(join17(rootDir, taskId), { recursive: true });
|
|
35827
|
+
atomicWriteSync(filePath, JSON.stringify(state, null, 2));
|
|
35828
|
+
} catch (err) {
|
|
35829
|
+
logger2.warn(
|
|
35830
|
+
{
|
|
35831
|
+
taskId,
|
|
35832
|
+
elementId: state.elementId,
|
|
35833
|
+
err: err instanceof Error ? err.message : String(err)
|
|
35834
|
+
},
|
|
35835
|
+
"preview_state_store: putState disk write failed"
|
|
35836
|
+
);
|
|
35837
|
+
}
|
|
35838
|
+
}
|
|
35839
|
+
function removeFromDisk(taskId, elementId) {
|
|
35840
|
+
const filePath = recordFilePath(rootDir, taskId, elementId);
|
|
35841
|
+
try {
|
|
35842
|
+
unlinkSync2(filePath);
|
|
35843
|
+
} catch (err) {
|
|
35844
|
+
if (!isEnoent2(err)) {
|
|
35845
|
+
logger2.warn(
|
|
35846
|
+
{ taskId, elementId, err: err instanceof Error ? err.message : String(err) },
|
|
35847
|
+
"preview_state_store: deleteByElementId disk unlink failed"
|
|
35848
|
+
);
|
|
35849
|
+
}
|
|
35850
|
+
}
|
|
35851
|
+
}
|
|
35852
|
+
return {
|
|
35853
|
+
getByElementId(taskId, elementId) {
|
|
35854
|
+
return registry.get(taskId)?.get(elementId);
|
|
35855
|
+
},
|
|
35856
|
+
findByProjectRoot(taskId, projectRoot) {
|
|
35857
|
+
const taskMap = registry.get(taskId);
|
|
35858
|
+
if (!taskMap) return void 0;
|
|
35859
|
+
for (const state of taskMap.values()) {
|
|
35860
|
+
if (state.projectRoot === projectRoot) return state;
|
|
35861
|
+
}
|
|
35862
|
+
return void 0;
|
|
35863
|
+
},
|
|
35864
|
+
putState(taskId, state) {
|
|
35865
|
+
writeToDisk(taskId, state);
|
|
35866
|
+
const taskMap = getTaskMap(taskId);
|
|
35867
|
+
taskMap.set(state.elementId, state);
|
|
35868
|
+
broadcastTask(taskId);
|
|
35869
|
+
},
|
|
35870
|
+
deleteByElementId(taskId, elementId) {
|
|
35871
|
+
const taskMap = registry.get(taskId);
|
|
35872
|
+
if (!taskMap) return;
|
|
35873
|
+
const existed = taskMap.has(elementId);
|
|
35874
|
+
if (!existed) return;
|
|
35875
|
+
removeFromDisk(taskId, elementId);
|
|
35876
|
+
taskMap.delete(elementId);
|
|
35877
|
+
if (taskMap.size === 0) registry.delete(taskId);
|
|
35878
|
+
broadcastTask(taskId);
|
|
35879
|
+
},
|
|
35880
|
+
listByTask(taskId) {
|
|
35881
|
+
const taskMap = registry.get(taskId);
|
|
35882
|
+
if (!taskMap) return [];
|
|
35883
|
+
const entries = [...taskMap.values()];
|
|
35884
|
+
entries.sort((a, b2) => a.elementId < b2.elementId ? -1 : a.elementId > b2.elementId ? 1 : 0);
|
|
35885
|
+
return entries;
|
|
35886
|
+
},
|
|
35887
|
+
setSendControlMessage(fn) {
|
|
35888
|
+
sendFn = fn;
|
|
35889
|
+
},
|
|
35890
|
+
broadcast(taskId) {
|
|
35891
|
+
broadcastTask(taskId);
|
|
35892
|
+
},
|
|
35893
|
+
dispose() {
|
|
35894
|
+
registry.clear();
|
|
35895
|
+
sendFn = null;
|
|
35896
|
+
}
|
|
35897
|
+
};
|
|
35898
|
+
}
|
|
35899
|
+
|
|
35576
35900
|
// src/services/preview-proxy.ts
|
|
35577
35901
|
import http2 from "http";
|
|
35578
35902
|
|
|
@@ -36449,30 +36773,31 @@ function createPreviewProxy(config2) {
|
|
|
36449
36773
|
}
|
|
36450
36774
|
|
|
36451
36775
|
// src/services/publish/published-artifact-store.ts
|
|
36452
|
-
import { createHash, randomUUID as
|
|
36453
|
-
import { mkdirSync as
|
|
36776
|
+
import { createHash, randomUUID as randomUUID3 } from "crypto";
|
|
36777
|
+
import { mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync6, watch as watch2 } from "fs";
|
|
36454
36778
|
import { mkdir as mkdir5, readFile as readFile10, rename as rename6, unlink as unlink4, writeFile as writeFile8 } from "fs/promises";
|
|
36455
|
-
import { dirname as
|
|
36779
|
+
import { dirname as dirname8, join as join18, relative as relative2 } from "path";
|
|
36780
|
+
import { isDeepStrictEqual } from "util";
|
|
36456
36781
|
var DEBOUNCE_MS2 = 200;
|
|
36457
36782
|
function bindMapKey(bindKind, bindKey) {
|
|
36458
36783
|
return `${bindKind}:${bindKey}`;
|
|
36459
36784
|
}
|
|
36460
|
-
function
|
|
36785
|
+
function recordFilePath2(rootDir, taskId, bindKind, bindKey) {
|
|
36461
36786
|
return join18(rootDir, taskId, bindKind, `${bindKey}.json`);
|
|
36462
36787
|
}
|
|
36463
36788
|
function sha256Hex(input) {
|
|
36464
36789
|
return createHash("sha256").update(input, "utf-8").digest("hex");
|
|
36465
36790
|
}
|
|
36466
36791
|
async function atomicWrite(filePath, content) {
|
|
36467
|
-
await mkdir5(
|
|
36468
|
-
const tmpPath = `${filePath}.${
|
|
36792
|
+
await mkdir5(dirname8(filePath), { recursive: true });
|
|
36793
|
+
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
36469
36794
|
await writeFile8(tmpPath, content, "utf-8");
|
|
36470
36795
|
await rename6(tmpPath, filePath);
|
|
36471
36796
|
}
|
|
36472
|
-
function
|
|
36797
|
+
function parseRecord2(filePath, logger2) {
|
|
36473
36798
|
let raw;
|
|
36474
36799
|
try {
|
|
36475
|
-
raw =
|
|
36800
|
+
raw = readFileSync6(filePath, "utf-8");
|
|
36476
36801
|
} catch {
|
|
36477
36802
|
return null;
|
|
36478
36803
|
}
|
|
@@ -36493,7 +36818,7 @@ function parseRecord(filePath, logger2) {
|
|
|
36493
36818
|
}
|
|
36494
36819
|
return result.data;
|
|
36495
36820
|
}
|
|
36496
|
-
function
|
|
36821
|
+
function isEnoent3(err) {
|
|
36497
36822
|
return err instanceof Error && Reflect.get(err, "code") === "ENOENT";
|
|
36498
36823
|
}
|
|
36499
36824
|
function createPublishedArtifactStore(opts) {
|
|
@@ -36501,20 +36826,19 @@ function createPublishedArtifactStore(opts) {
|
|
|
36501
36826
|
const registry = /* @__PURE__ */ new Map();
|
|
36502
36827
|
let sendFn = null;
|
|
36503
36828
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
36504
|
-
const ownWrites = /* @__PURE__ */ new Set();
|
|
36505
36829
|
let watcher = null;
|
|
36506
36830
|
let disposed = false;
|
|
36507
|
-
|
|
36831
|
+
mkdirSync3(rootDir, { recursive: true });
|
|
36508
36832
|
function scanKindDir(taskId, kindPath) {
|
|
36509
36833
|
let files;
|
|
36510
36834
|
try {
|
|
36511
|
-
files =
|
|
36835
|
+
files = readdirSync2(kindPath);
|
|
36512
36836
|
} catch {
|
|
36513
36837
|
return;
|
|
36514
36838
|
}
|
|
36515
36839
|
for (const file of files) {
|
|
36516
36840
|
if (!file.endsWith(".json")) continue;
|
|
36517
|
-
const record =
|
|
36841
|
+
const record = parseRecord2(join18(kindPath, file), logger2);
|
|
36518
36842
|
if (record) inMemorySet(taskId, record);
|
|
36519
36843
|
}
|
|
36520
36844
|
}
|
|
@@ -36522,7 +36846,7 @@ function createPublishedArtifactStore(opts) {
|
|
|
36522
36846
|
const taskPath = join18(rootDir, taskId);
|
|
36523
36847
|
let kindDirs;
|
|
36524
36848
|
try {
|
|
36525
|
-
kindDirs =
|
|
36849
|
+
kindDirs = readdirSync2(taskPath);
|
|
36526
36850
|
} catch {
|
|
36527
36851
|
return;
|
|
36528
36852
|
}
|
|
@@ -36534,7 +36858,7 @@ function createPublishedArtifactStore(opts) {
|
|
|
36534
36858
|
function scanRootDir() {
|
|
36535
36859
|
let taskDirs;
|
|
36536
36860
|
try {
|
|
36537
|
-
taskDirs =
|
|
36861
|
+
taskDirs = readdirSync2(rootDir);
|
|
36538
36862
|
} catch {
|
|
36539
36863
|
return;
|
|
36540
36864
|
}
|
|
@@ -36580,10 +36904,6 @@ function createPublishedArtifactStore(opts) {
|
|
|
36580
36904
|
return { taskId, bindKind: kindStr, bindKey };
|
|
36581
36905
|
}
|
|
36582
36906
|
function scheduleFileEvent(filePath) {
|
|
36583
|
-
if (ownWrites.has(filePath)) {
|
|
36584
|
-
ownWrites.delete(filePath);
|
|
36585
|
-
return;
|
|
36586
|
-
}
|
|
36587
36907
|
const prev = debounceTimers.get(filePath);
|
|
36588
36908
|
if (prev) clearTimeout(prev);
|
|
36589
36909
|
debounceTimers.set(
|
|
@@ -36607,6 +36927,8 @@ function createPublishedArtifactStore(opts) {
|
|
|
36607
36927
|
try {
|
|
36608
36928
|
raw = await readFile10(filePath, "utf-8");
|
|
36609
36929
|
} catch {
|
|
36930
|
+
const existing2 = registry.get(taskId)?.get(bindMapKey(bindKind, bindKey));
|
|
36931
|
+
if (!existing2) return;
|
|
36610
36932
|
inMemoryDelete(taskId, bindKind, bindKey);
|
|
36611
36933
|
broadcastTask(taskId);
|
|
36612
36934
|
return;
|
|
@@ -36626,6 +36948,8 @@ function createPublishedArtifactStore(opts) {
|
|
|
36626
36948
|
);
|
|
36627
36949
|
return;
|
|
36628
36950
|
}
|
|
36951
|
+
const existing = registry.get(taskId)?.get(bindMapKey(bindKind, bindKey));
|
|
36952
|
+
if (existing && isDeepStrictEqual(existing, result.data)) return;
|
|
36629
36953
|
inMemorySet(taskId, result.data);
|
|
36630
36954
|
broadcastTask(taskId);
|
|
36631
36955
|
}
|
|
@@ -36658,27 +36982,17 @@ function createPublishedArtifactStore(opts) {
|
|
|
36658
36982
|
return entries;
|
|
36659
36983
|
},
|
|
36660
36984
|
async putBinding(taskId, record) {
|
|
36661
|
-
const filePath =
|
|
36662
|
-
|
|
36663
|
-
try {
|
|
36664
|
-
await atomicWrite(filePath, JSON.stringify(record, null, 2));
|
|
36665
|
-
} catch (err) {
|
|
36666
|
-
ownWrites.delete(filePath);
|
|
36667
|
-
throw err;
|
|
36668
|
-
}
|
|
36985
|
+
const filePath = recordFilePath2(rootDir, taskId, record.bindKind, record.bindKey);
|
|
36986
|
+
await atomicWrite(filePath, JSON.stringify(record, null, 2));
|
|
36669
36987
|
inMemorySet(taskId, record);
|
|
36670
36988
|
broadcastTask(taskId);
|
|
36671
36989
|
},
|
|
36672
36990
|
async deleteBinding(taskId, bindKind, bindKey) {
|
|
36673
|
-
const filePath =
|
|
36674
|
-
ownWrites.add(filePath);
|
|
36991
|
+
const filePath = recordFilePath2(rootDir, taskId, bindKind, bindKey);
|
|
36675
36992
|
try {
|
|
36676
36993
|
await unlink4(filePath);
|
|
36677
36994
|
} catch (err) {
|
|
36678
|
-
if (!
|
|
36679
|
-
ownWrites.delete(filePath);
|
|
36680
|
-
throw err;
|
|
36681
|
-
}
|
|
36995
|
+
if (!isEnoent3(err)) throw err;
|
|
36682
36996
|
}
|
|
36683
36997
|
inMemoryDelete(taskId, bindKind, bindKey);
|
|
36684
36998
|
broadcastTask(taskId);
|
|
@@ -37878,44 +38192,51 @@ async function storePreviewArtifact(input, bindKey, resolvedProjectRoot, existin
|
|
|
37878
38192
|
expiresAt: result.expiresAt,
|
|
37879
38193
|
ttl: input.ttl,
|
|
37880
38194
|
lastSize: files.reduce((sum, f2) => sum + f2.content.length, 0),
|
|
37881
|
-
projectRoot: resolvedProjectRoot
|
|
38195
|
+
projectRoot: resolvedProjectRoot,
|
|
37882
38196
|
canvasElementId: input.target.kind === "previewPort" ? input.target.canvasElementId : void 0
|
|
37883
38197
|
};
|
|
37884
38198
|
await input.publishedArtifactStore.putBinding(input.taskId, newRecord);
|
|
37885
38199
|
}
|
|
38200
|
+
function resolvePreviewProjectRoot(input, target) {
|
|
38201
|
+
const canvasElementId = target.canvasElementId;
|
|
38202
|
+
const storeEntry = canvasElementId && input.previewStateStore ? input.previewStateStore.getByElementId(input.taskId, canvasElementId) ?? null : null;
|
|
38203
|
+
const storeProjectRoot = storeEntry?.projectRoot ?? null;
|
|
38204
|
+
const detectedPortMatch = input.getDetectedPorts().find((p2) => p2.port === target.previewPort);
|
|
38205
|
+
const detectedProjectRoot = detectedPortMatch?.projectRoot ?? null;
|
|
38206
|
+
const resolvedProjectRoot = storeProjectRoot ?? detectedProjectRoot ?? null;
|
|
38207
|
+
const resolvedFrom = storeProjectRoot ? "store" : detectedProjectRoot ? "detected_ports" : "none";
|
|
38208
|
+
return { resolvedProjectRoot, resolvedFrom, storeProjectRoot, detectedProjectRoot };
|
|
38209
|
+
}
|
|
37886
38210
|
async function runPublishPreviewPort(input, ctx) {
|
|
37887
38211
|
if (input.target.kind !== "previewPort") {
|
|
37888
38212
|
return { ok: false, phase: "failed", error: "Internal error: wrong target kind" };
|
|
37889
38213
|
}
|
|
37890
|
-
const
|
|
37891
|
-
const
|
|
37892
|
-
const
|
|
37893
|
-
const resolvedProjectRoot =
|
|
37894
|
-
const bindKey = resolvedProjectRoot !== null ? sha256Hex(resolvedProjectRoot) : null;
|
|
37895
|
-
const existing = bindKey !== null ? input.publishedArtifactStore?.getBinding(input.taskId, "preview", bindKey) ?? null : null;
|
|
37896
|
-
const buildRoot = resolvedProjectRoot ?? input.projectRoot;
|
|
38214
|
+
const target = input.target;
|
|
38215
|
+
const canvasElementId = target.canvasElementId;
|
|
38216
|
+
const resolution = resolvePreviewProjectRoot(input, target);
|
|
38217
|
+
const { resolvedProjectRoot } = resolution;
|
|
37897
38218
|
ctx.log?.({
|
|
37898
38219
|
event: "publish_preview_resolved",
|
|
37899
|
-
|
|
37900
|
-
|
|
38220
|
+
canvasElementId: canvasElementId ?? null,
|
|
38221
|
+
previewPort: target.previewPort,
|
|
38222
|
+
storeProjectRoot: resolution.storeProjectRoot,
|
|
38223
|
+
detectedPortProjectRoot: resolution.detectedProjectRoot,
|
|
37901
38224
|
resolvedProjectRoot,
|
|
37902
|
-
|
|
38225
|
+
resolvedFrom: resolution.resolvedFrom
|
|
37903
38226
|
});
|
|
37904
|
-
|
|
38227
|
+
if (!resolvedProjectRoot) {
|
|
38228
|
+
const error2 = canvasElementId ? `Preview element has no resolvable projectRoot \u2014 was \`present({projectRoot})\` called? (canvasElementId: ${canvasElementId})` : "Preview publish requires a canvasElementId. The browser must send the stable canvas element id so the daemon can resolve projectRoot from the preview state store.";
|
|
38229
|
+
ctx.onProgress("failed", error2);
|
|
38230
|
+
return { ok: false, phase: "detecting_framework", error: error2 };
|
|
38231
|
+
}
|
|
38232
|
+
const bindKey = sha256Hex(resolvedProjectRoot);
|
|
38233
|
+
const existing = input.publishedArtifactStore?.getBinding(input.taskId, "preview", bindKey) ?? null;
|
|
38234
|
+
const built = await detectAndBuild(resolvedProjectRoot, ctx);
|
|
37905
38235
|
if (!built.ok) return built.outcome;
|
|
37906
38236
|
ctx.onProgress("uploading");
|
|
37907
38237
|
try {
|
|
37908
38238
|
const result = await uploadPreviewFiles(input, built.framework, built.files, existing);
|
|
37909
|
-
|
|
37910
|
-
await storePreviewArtifact(
|
|
37911
|
-
input,
|
|
37912
|
-
bindKey,
|
|
37913
|
-
resolvedProjectRoot,
|
|
37914
|
-
existing,
|
|
37915
|
-
result,
|
|
37916
|
-
built.files
|
|
37917
|
-
);
|
|
37918
|
-
}
|
|
38239
|
+
await storePreviewArtifact(input, bindKey, resolvedProjectRoot, existing, result, built.files);
|
|
37919
38240
|
ctx.onProgress("completed");
|
|
37920
38241
|
return { ok: true, id: result.id, url: result.url, expiresAt: result.expiresAt };
|
|
37921
38242
|
} catch (err) {
|
|
@@ -37966,6 +38287,7 @@ function createPublishTools(ctx) {
|
|
|
37966
38287
|
taskId: ctx.taskId.current,
|
|
37967
38288
|
vizWatcher: ctx.vizWatcher,
|
|
37968
38289
|
publishedArtifactStore: ctx.publishedArtifactStore,
|
|
38290
|
+
previewStateStore: ctx.previewStateStore,
|
|
37969
38291
|
getDetectedPorts: ctx.getDetectedPorts
|
|
37970
38292
|
},
|
|
37971
38293
|
{ onProgress: () => {
|
|
@@ -38068,6 +38390,7 @@ async function handlePublishRequest(params) {
|
|
|
38068
38390
|
taskId: params.taskId,
|
|
38069
38391
|
vizWatcher: params.vizWatcher,
|
|
38070
38392
|
publishedArtifactStore: params.publishedArtifactStore,
|
|
38393
|
+
previewStateStore: params.previewStateStore,
|
|
38071
38394
|
getDetectedPorts: params.getDetectedPorts
|
|
38072
38395
|
},
|
|
38073
38396
|
{ onProgress: emitProgress, ...params.log !== void 0 && { log: params.log } }
|
|
@@ -39178,38 +39501,22 @@ async function rehydrateCollabQueues(persistence, taskManager, log) {
|
|
|
39178
39501
|
})
|
|
39179
39502
|
);
|
|
39180
39503
|
}
|
|
39181
|
-
function
|
|
39182
|
-
return
|
|
39183
|
-
}
|
|
39184
|
-
function collectOwnedPreviews(canvasRepo, taskId, selfUserId) {
|
|
39185
|
-
try {
|
|
39186
|
-
const elements = canvasRepo.getElements(taskId);
|
|
39187
|
-
const ports = [];
|
|
39188
|
-
for (const el of elements) {
|
|
39189
|
-
if (el.data.type !== "preview") continue;
|
|
39190
|
-
const data = isPreviewData(el.data.data) ? el.data.data : null;
|
|
39191
|
-
if (!data || data.ownerUserId !== selfUserId) continue;
|
|
39192
|
-
if (typeof data.port === "number") ports.push(data.port);
|
|
39193
|
-
}
|
|
39194
|
-
return ports;
|
|
39195
|
-
} catch {
|
|
39196
|
-
return [];
|
|
39197
|
-
}
|
|
39504
|
+
function collectOwnedPreviews(_canvasRepo, _taskId, _selfUserId) {
|
|
39505
|
+
return [];
|
|
39198
39506
|
}
|
|
39199
39507
|
async function rehydrateCanvasPreviews(canvasRepo, previewProxy, selfUserId, taskStateStore, log) {
|
|
39200
39508
|
const tasks = await taskStateStore.listTasks();
|
|
39201
39509
|
const refs = [];
|
|
39202
39510
|
for (const taskId of Object.keys(tasks)) {
|
|
39203
39511
|
if (!await canvasRepo.hasLocalDoc(taskId)) continue;
|
|
39204
|
-
for (const
|
|
39205
|
-
refs.push(
|
|
39512
|
+
for (const ref of collectOwnedPreviews(canvasRepo, taskId, selfUserId)) {
|
|
39513
|
+
refs.push(ref);
|
|
39206
39514
|
}
|
|
39207
39515
|
}
|
|
39208
39516
|
const warmedTaskIds = /* @__PURE__ */ new Set();
|
|
39209
39517
|
for (const { taskId, port } of refs) {
|
|
39210
39518
|
try {
|
|
39211
|
-
|
|
39212
|
-
await reconcileProxyPortInCanvas(canvasRepo, taskId, port, newProxyPort);
|
|
39519
|
+
await previewProxy.acquire(port);
|
|
39213
39520
|
warmedTaskIds.add(taskId);
|
|
39214
39521
|
log({ event: "canvas_preview_warmed", taskId, port });
|
|
39215
39522
|
} catch (err) {
|
|
@@ -39223,19 +39530,6 @@ async function rehydrateCanvasPreviews(canvasRepo, previewProxy, selfUserId, tas
|
|
|
39223
39530
|
}
|
|
39224
39531
|
return Array.from(warmedTaskIds);
|
|
39225
39532
|
}
|
|
39226
|
-
async function reconcileProxyPortInCanvas(canvasRepo, taskId, upstreamPort, newProxyPort) {
|
|
39227
|
-
if (!await canvasRepo.hasLocalDoc(taskId)) return;
|
|
39228
|
-
for (const el of canvasRepo.getElements(taskId)) {
|
|
39229
|
-
if (el.data.type !== "preview") continue;
|
|
39230
|
-
const rec = el.data.data;
|
|
39231
|
-
if (!isPreviewData(rec)) continue;
|
|
39232
|
-
if (rec.port !== upstreamPort) continue;
|
|
39233
|
-
if (rec.proxyPort === newProxyPort) continue;
|
|
39234
|
-
canvasRepo.updateElement(taskId, el.id, {
|
|
39235
|
-
data: { ...rec, proxyPort: newProxyPort }
|
|
39236
|
-
});
|
|
39237
|
-
}
|
|
39238
|
-
}
|
|
39239
39533
|
async function migratePinnedToFavoriteTasks(taskStateStore, userSettingsStore, log) {
|
|
39240
39534
|
const settings = await userSettingsStore.getSettings();
|
|
39241
39535
|
if (settings.favoriteTasks.length > 0) return;
|
|
@@ -39245,6 +39539,77 @@ async function migratePinnedToFavoriteTasks(taskStateStore, userSettingsStore, l
|
|
|
39245
39539
|
await userSettingsStore.updateSetting("favoriteTasks", pinnedIds);
|
|
39246
39540
|
log({ event: "pinned_to_favorites_migrated", count: pinnedIds.length });
|
|
39247
39541
|
}
|
|
39542
|
+
function isObjectRecord(v2) {
|
|
39543
|
+
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
39544
|
+
}
|
|
39545
|
+
function extractLegacyPreviewState(elementId, rawData) {
|
|
39546
|
+
if (!isObjectRecord(rawData)) return null;
|
|
39547
|
+
const ownerUserId = typeof rawData.ownerUserId === "string" && rawData.ownerUserId.length > 0 ? rawData.ownerUserId : null;
|
|
39548
|
+
if (!ownerUserId) return null;
|
|
39549
|
+
const state = { elementId, ownerUserId };
|
|
39550
|
+
let hasLegacyField = false;
|
|
39551
|
+
if (typeof rawData.port === "number" && Number.isInteger(rawData.port) && rawData.port > 0) {
|
|
39552
|
+
state.port = rawData.port;
|
|
39553
|
+
hasLegacyField = true;
|
|
39554
|
+
}
|
|
39555
|
+
if (typeof rawData.url === "string" && rawData.url.length > 0) {
|
|
39556
|
+
state.url = rawData.url;
|
|
39557
|
+
hasLegacyField = true;
|
|
39558
|
+
}
|
|
39559
|
+
if (typeof rawData.proxyPort === "number" && Number.isInteger(rawData.proxyPort) && rawData.proxyPort > 0) {
|
|
39560
|
+
state.proxyPort = rawData.proxyPort;
|
|
39561
|
+
hasLegacyField = true;
|
|
39562
|
+
}
|
|
39563
|
+
if (typeof rawData.initialPath === "string" && rawData.initialPath.length > 0) {
|
|
39564
|
+
state.initialPath = rawData.initialPath;
|
|
39565
|
+
hasLegacyField = true;
|
|
39566
|
+
}
|
|
39567
|
+
if (typeof rawData.projectRoot === "string" && rawData.projectRoot.length > 0) {
|
|
39568
|
+
state.projectRoot = rawData.projectRoot;
|
|
39569
|
+
hasLegacyField = true;
|
|
39570
|
+
}
|
|
39571
|
+
return hasLegacyField ? state : null;
|
|
39572
|
+
}
|
|
39573
|
+
function migrateLegacyPreviewDataForTask(taskId, canvasRepo, store, log) {
|
|
39574
|
+
let seededCount = 0;
|
|
39575
|
+
const elements = canvasRepo.getElements(taskId);
|
|
39576
|
+
for (const el of elements) {
|
|
39577
|
+
if (el.data.type !== "preview") continue;
|
|
39578
|
+
const elementId = String(el.id);
|
|
39579
|
+
if (store.getByElementId(taskId, elementId)) continue;
|
|
39580
|
+
const legacy = extractLegacyPreviewState(elementId, el.data.data);
|
|
39581
|
+
if (!legacy) continue;
|
|
39582
|
+
store.putState(taskId, legacy);
|
|
39583
|
+
seededCount += 1;
|
|
39584
|
+
log({
|
|
39585
|
+
event: "preview_legacy_migrated",
|
|
39586
|
+
taskId,
|
|
39587
|
+
elementId,
|
|
39588
|
+
port: legacy.port,
|
|
39589
|
+
projectRoot: legacy.projectRoot
|
|
39590
|
+
});
|
|
39591
|
+
}
|
|
39592
|
+
return seededCount;
|
|
39593
|
+
}
|
|
39594
|
+
async function migrateLegacyPreviewData(canvasRepo, previewStateStore, taskStateStore, log) {
|
|
39595
|
+
const tasks = await taskStateStore.listTasks();
|
|
39596
|
+
let totalSeeded = 0;
|
|
39597
|
+
for (const taskId of Object.keys(tasks)) {
|
|
39598
|
+
if (!await canvasRepo.hasLocalDoc(taskId)) continue;
|
|
39599
|
+
try {
|
|
39600
|
+
totalSeeded += migrateLegacyPreviewDataForTask(taskId, canvasRepo, previewStateStore, log);
|
|
39601
|
+
} catch (err) {
|
|
39602
|
+
log({
|
|
39603
|
+
event: "preview_legacy_migrate_task_failed",
|
|
39604
|
+
taskId,
|
|
39605
|
+
error: err instanceof Error ? err.message : String(err)
|
|
39606
|
+
});
|
|
39607
|
+
}
|
|
39608
|
+
}
|
|
39609
|
+
if (totalSeeded > 0) {
|
|
39610
|
+
log({ event: "preview_legacy_migrate_complete", seededCount: totalSeeded });
|
|
39611
|
+
}
|
|
39612
|
+
}
|
|
39248
39613
|
async function sweepStaleTasks(taskStateStore, taskManager, log) {
|
|
39249
39614
|
const tasks = await taskStateStore.listTasks();
|
|
39250
39615
|
const actions = planStaleSweep(tasks, (id) => taskManager.isRunning(id));
|
|
@@ -39372,7 +39737,7 @@ function assertNever3(value) {
|
|
|
39372
39737
|
|
|
39373
39738
|
// src/services/bootstrap/self-update-lock.ts
|
|
39374
39739
|
import { mkdir as mkdir7, readFile as readFile16, stat as stat5, unlink as unlink6, writeFile as writeFile11 } from "fs/promises";
|
|
39375
|
-
import { dirname as
|
|
39740
|
+
import { dirname as dirname9, join as join24 } from "path";
|
|
39376
39741
|
var LOCK_FILENAME = ".lock";
|
|
39377
39742
|
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
39378
39743
|
var LockFileSchema = external_exports.object({
|
|
@@ -39464,7 +39829,7 @@ async function resolveEexist(shipyardHome, path2, ownerPid, now, isProcessAlive2
|
|
|
39464
39829
|
}
|
|
39465
39830
|
async function tryAcquireLockExclusive(shipyardHome, pid, now, isProcessAlive2) {
|
|
39466
39831
|
const path2 = lockPath(shipyardHome);
|
|
39467
|
-
await mkdir7(
|
|
39832
|
+
await mkdir7(dirname9(path2), { recursive: true });
|
|
39468
39833
|
const body = JSON.stringify({ pid, startedAt: now() });
|
|
39469
39834
|
while (true) {
|
|
39470
39835
|
try {
|
|
@@ -40025,7 +40390,7 @@ function createCanvasResourceResolver(deps) {
|
|
|
40025
40390
|
|
|
40026
40391
|
// src/services/credentials/vault-key-manager.ts
|
|
40027
40392
|
import { mkdir as mkdir8, readFile as readFile17, writeFile as writeFile12 } from "fs/promises";
|
|
40028
|
-
import { dirname as
|
|
40393
|
+
import { dirname as dirname10, join as join26 } from "path";
|
|
40029
40394
|
|
|
40030
40395
|
// src/services/credentials/vault-crypto.ts
|
|
40031
40396
|
async function generateVaultKey() {
|
|
@@ -40111,12 +40476,12 @@ async function resolveMachineId(shipyardHome) {
|
|
|
40111
40476
|
} catch {
|
|
40112
40477
|
}
|
|
40113
40478
|
const id = globalThis.crypto.randomUUID();
|
|
40114
|
-
await mkdir8(
|
|
40479
|
+
await mkdir8(dirname10(idPath), { recursive: true });
|
|
40115
40480
|
await writeFile12(idPath, id, { encoding: "utf-8", mode: 384 });
|
|
40116
40481
|
return id;
|
|
40117
40482
|
}
|
|
40118
40483
|
async function saveKeyLocally(keyPath, key) {
|
|
40119
|
-
await mkdir8(
|
|
40484
|
+
await mkdir8(dirname10(keyPath), { recursive: true });
|
|
40120
40485
|
await writeFile12(keyPath, key, { encoding: "utf-8", mode: 384 });
|
|
40121
40486
|
}
|
|
40122
40487
|
|
|
@@ -40814,7 +41179,7 @@ function createGitCheckpointService() {
|
|
|
40814
41179
|
import { createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
40815
41180
|
|
|
40816
41181
|
// src/services/harness/comment-server.ts
|
|
40817
|
-
import { randomUUID as
|
|
41182
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
40818
41183
|
import { access as access2, readFile as readFile20 } from "fs/promises";
|
|
40819
41184
|
import { join as join29, relative as relative3 } from "path";
|
|
40820
41185
|
var TOOL_DESCRIPTION = [
|
|
@@ -40933,7 +41298,7 @@ function resolveShortCommentId(annotations, input) {
|
|
|
40933
41298
|
}
|
|
40934
41299
|
function buildReplyAnnotation(parent, body) {
|
|
40935
41300
|
const base3 = {
|
|
40936
|
-
commentId:
|
|
41301
|
+
commentId: randomUUID4(),
|
|
40937
41302
|
body,
|
|
40938
41303
|
authorName: "Claude Code",
|
|
40939
41304
|
authorKind: "agent",
|
|
@@ -41235,7 +41600,7 @@ async function handleReplyComment(ctx, parentCommentId, comment2) {
|
|
|
41235
41600
|
async function handlePlanComment(ctx, anchorText, comment2) {
|
|
41236
41601
|
const annotation = {
|
|
41237
41602
|
annotationType: "plan-text",
|
|
41238
|
-
commentId:
|
|
41603
|
+
commentId: randomUUID4(),
|
|
41239
41604
|
anchorText,
|
|
41240
41605
|
anchorContext: null,
|
|
41241
41606
|
body: comment2,
|
|
@@ -41285,7 +41650,7 @@ async function handleDiffComment(ctx, filePath, lineNumber, comment2) {
|
|
|
41285
41650
|
const lineContentHash = lineContent ? djb2Hash(lineContent) : "";
|
|
41286
41651
|
const annotation = {
|
|
41287
41652
|
annotationType: "diff-hunk",
|
|
41288
|
-
commentId:
|
|
41653
|
+
commentId: randomUUID4(),
|
|
41289
41654
|
filePath: normalizedPath,
|
|
41290
41655
|
lineNumber,
|
|
41291
41656
|
lineContent,
|
|
@@ -41977,7 +42342,7 @@ var PresentPortSchema = external_exports.object({
|
|
|
41977
42342
|
width: external_exports.number().int().positive().optional().describe("Canvas only."),
|
|
41978
42343
|
height: external_exports.number().int().positive().optional().describe("Canvas only."),
|
|
41979
42344
|
projectRoot: external_exports.string().optional().describe(
|
|
41980
|
-
"Absolute path to the dev server's package.json directory.
|
|
42345
|
+
"Absolute path to the dev server's package.json directory. Binds this preview to a stable app identity. Restarting your dev server on a new port (or a fresh `present` call) with the same projectRoot will reuse the same canvas element \u2014 no duplicate frames, and the published URL is stable across port restarts. Without projectRoot, every port change creates a new element."
|
|
41981
42346
|
)
|
|
41982
42347
|
});
|
|
41983
42348
|
var PresentUrlSchema = external_exports.object({
|
|
@@ -42006,7 +42371,7 @@ var PresentToolInputShape = {
|
|
|
42006
42371
|
height: external_exports.number().int().positive().optional().describe("Canvas only; ignored elsewhere."),
|
|
42007
42372
|
initialPath: external_exports.string().optional().describe('SPA route for source="port" on canvas (e.g. "/settings").'),
|
|
42008
42373
|
projectRoot: external_exports.string().optional().describe(
|
|
42009
|
-
`Absolute path to the dev server's package.json dir (source="port" only).
|
|
42374
|
+
`Absolute path to the dev server's package.json dir (source="port" only). Binds this preview to a stable app identity. Restarting your dev server on a new port with the same projectRoot will reuse the same canvas element \u2014 no duplicate frames, and the published URL is stable across port restarts. Without projectRoot, every port change creates a new element.`
|
|
42010
42375
|
),
|
|
42011
42376
|
force: external_exports.boolean().optional().describe("Re-apply an already-placed slug to canvas after editing. Slug+canvas only.")
|
|
42012
42377
|
};
|
|
@@ -51285,6 +51650,7 @@ function createHarnessMcpServer(ctx) {
|
|
|
51285
51650
|
projectRoot: ctx.projectRoot,
|
|
51286
51651
|
previewProxy: ctx.previewProxy,
|
|
51287
51652
|
publishedArtifactStore: ctx.publishedArtifactStore,
|
|
51653
|
+
previewStateStore: ctx.previewStateStore,
|
|
51288
51654
|
getDetectedPorts: ctx.getDetectedPorts
|
|
51289
51655
|
};
|
|
51290
51656
|
const server = createSdkMcpServer({
|
|
@@ -51307,37 +51673,53 @@ function createHarnessMcpServer(ctx) {
|
|
|
51307
51673
|
}
|
|
51308
51674
|
|
|
51309
51675
|
// src/services/harness/visualization-file-watcher.ts
|
|
51310
|
-
import { randomUUID as
|
|
51676
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
51311
51677
|
import { watch as watch3 } from "fs";
|
|
51312
51678
|
import { mkdir as mkdir10, readFile as readFile22, rename as rename9, writeFile as writeFile15 } from "fs/promises";
|
|
51313
|
-
import { dirname as
|
|
51679
|
+
import { dirname as dirname11, join as join31 } from "path";
|
|
51314
51680
|
var PREVIEW_DEFAULT_W = 1200;
|
|
51315
51681
|
var PREVIEW_DEFAULT_H = 800;
|
|
51316
51682
|
function previewDataToLoroValue(data) {
|
|
51317
|
-
|
|
51318
|
-
if (data.port !== void 0) out.port = data.port;
|
|
51319
|
-
if (data.url !== void 0) out.url = data.url;
|
|
51320
|
-
if (data.proxyPort !== void 0) out.proxyPort = data.proxyPort;
|
|
51321
|
-
if (data.initialPath !== void 0) out.initialPath = data.initialPath;
|
|
51322
|
-
return out;
|
|
51683
|
+
return { ownerUserId: data.ownerUserId };
|
|
51323
51684
|
}
|
|
51324
|
-
function
|
|
51685
|
+
function isObjectRecord2(v2) {
|
|
51325
51686
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
51326
51687
|
}
|
|
51327
51688
|
function extractPreviewData(raw) {
|
|
51328
|
-
if (!
|
|
51689
|
+
if (!isObjectRecord2(raw)) return null;
|
|
51329
51690
|
const out = {};
|
|
51330
|
-
if (typeof raw.port === "number") out.port = raw.port;
|
|
51331
|
-
if (typeof raw.url === "string") out.url = raw.url;
|
|
51332
|
-
if (typeof raw.proxyPort === "number") out.proxyPort = raw.proxyPort;
|
|
51333
51691
|
if (typeof raw.ownerUserId === "string") out.ownerUserId = raw.ownerUserId;
|
|
51334
|
-
|
|
51692
|
+
return out;
|
|
51693
|
+
}
|
|
51694
|
+
function buildPreviewState(elementId, params, ownerUserId, existing) {
|
|
51695
|
+
const state = { elementId, ownerUserId };
|
|
51696
|
+
const port = params.port ?? existing?.port;
|
|
51697
|
+
if (port !== void 0) state.port = port;
|
|
51698
|
+
const url = params.url ?? existing?.url;
|
|
51699
|
+
if (url !== void 0) state.url = url;
|
|
51700
|
+
const proxyPort = params.proxyPort ?? existing?.proxyPort;
|
|
51701
|
+
if (proxyPort !== void 0) state.proxyPort = proxyPort;
|
|
51702
|
+
const initialPath = params.initialPath ?? existing?.initialPath;
|
|
51703
|
+
if (initialPath !== void 0) state.initialPath = initialPath;
|
|
51704
|
+
const projectRoot = params.projectRoot ?? existing?.projectRoot;
|
|
51705
|
+
if (typeof projectRoot === "string" && projectRoot.length > 0) state.projectRoot = projectRoot;
|
|
51706
|
+
return state;
|
|
51707
|
+
}
|
|
51708
|
+
function mergedPreviewData(data, state) {
|
|
51709
|
+
const out = {
|
|
51710
|
+
ownerUserId: data.ownerUserId
|
|
51711
|
+
};
|
|
51712
|
+
if (state.port !== void 0) out.port = state.port;
|
|
51713
|
+
if (state.url !== void 0) out.url = state.url;
|
|
51714
|
+
if (state.proxyPort !== void 0) out.proxyPort = state.proxyPort;
|
|
51715
|
+
if (state.initialPath !== void 0) out.initialPath = state.initialPath;
|
|
51716
|
+
if (state.projectRoot !== void 0) out.projectRoot = state.projectRoot;
|
|
51335
51717
|
return out;
|
|
51336
51718
|
}
|
|
51337
51719
|
var DEBOUNCE_MS3 = 200;
|
|
51338
51720
|
async function atomicWrite2(filePath, content) {
|
|
51339
|
-
await mkdir10(
|
|
51340
|
-
const tmpPath = `${filePath}.${
|
|
51721
|
+
await mkdir10(dirname11(filePath), { recursive: true });
|
|
51722
|
+
const tmpPath = `${filePath}.${randomUUID5()}.tmp`;
|
|
51341
51723
|
await writeFile15(tmpPath, content, "utf-8");
|
|
51342
51724
|
await rename9(tmpPath, filePath);
|
|
51343
51725
|
}
|
|
@@ -51506,7 +51888,7 @@ var VisualizationFileWatcher = class {
|
|
|
51506
51888
|
if (this.#disposed) return;
|
|
51507
51889
|
if (this.#watchedFiles.has(filePath)) return;
|
|
51508
51890
|
this.#watchedFiles.add(filePath);
|
|
51509
|
-
const dirPath =
|
|
51891
|
+
const dirPath = dirname11(filePath);
|
|
51510
51892
|
const existing = this.#dirWatchers.get(dirPath);
|
|
51511
51893
|
if (existing) {
|
|
51512
51894
|
existing.refCount += 1;
|
|
@@ -51546,7 +51928,7 @@ var VisualizationFileWatcher = class {
|
|
|
51546
51928
|
}
|
|
51547
51929
|
#stopFileWatch(filePath) {
|
|
51548
51930
|
if (!this.#watchedFiles.delete(filePath)) return;
|
|
51549
|
-
const dirPath =
|
|
51931
|
+
const dirPath = dirname11(filePath);
|
|
51550
51932
|
const entry = this.#dirWatchers.get(dirPath);
|
|
51551
51933
|
if (entry) {
|
|
51552
51934
|
entry.refCount -= 1;
|
|
@@ -51564,39 +51946,56 @@ var VisualizationFileWatcher = class {
|
|
|
51564
51946
|
/**
|
|
51565
51947
|
* Idempotent create-or-update of a preview canvas element.
|
|
51566
51948
|
*
|
|
51567
|
-
*
|
|
51568
|
-
*
|
|
51569
|
-
*
|
|
51949
|
+
* Dedupe order (pointer-only CRDT, PROTOCOL_VERSION 36):
|
|
51950
|
+
* 1. If params has `projectRoot` AND `previewStateStore` already has
|
|
51951
|
+
* an entry with a matching `projectRoot` → update that entry's
|
|
51952
|
+
* port / url / proxyPort in place (same elementId, no new canvas
|
|
51953
|
+
* element). This is the port-swap upsert: Vite restarts on a new
|
|
51954
|
+
* port, daemon calls `present({projectRoot})` again, and the
|
|
51955
|
+
* existing element's iframe URL flips — no duplicates.
|
|
51956
|
+
* 2. Else if the CRDT has a `preview` element with matching
|
|
51957
|
+
* `ownerUserId` AND the store has an entry with matching port —
|
|
51958
|
+
* update that entry. This covers the legacy / no-projectRoot path
|
|
51959
|
+
* where dedupe keys off the live port alone.
|
|
51960
|
+
* 3. Else create a new canvas element (pointer-only) + a new store
|
|
51961
|
+
* entry carrying the mutable state.
|
|
51570
51962
|
*
|
|
51571
|
-
*
|
|
51572
|
-
*
|
|
51573
|
-
*
|
|
51963
|
+
* In every case, the store is written with the element's projectRoot
|
|
51964
|
+
* (if known) so the publish pipeline can look it up by elementId.
|
|
51965
|
+
*
|
|
51966
|
+
* loro-crdt mutations are synchronous via `change()`, so the
|
|
51967
|
+
* check+write against the CRDT cannot race a concurrent caller.
|
|
51574
51968
|
*/
|
|
51575
51969
|
createOrUpdatePreviewElement(params) {
|
|
51576
51970
|
const data = this.registry.planPreviewElement(params);
|
|
51577
51971
|
const loroData = previewDataToLoroValue(data);
|
|
51972
|
+
const store = this.#deps.previewStateStore ?? null;
|
|
51973
|
+
if (store && params.projectRoot !== void 0 && params.projectRoot.length > 0) {
|
|
51974
|
+
const existing = store.findByProjectRoot(params.taskId, params.projectRoot);
|
|
51975
|
+
if (existing) {
|
|
51976
|
+
const elementId2 = existing.elementId;
|
|
51977
|
+
const mergedState = buildPreviewState(elementId2, params, data.ownerUserId, existing);
|
|
51978
|
+
store.putState(params.taskId, mergedState);
|
|
51979
|
+
return {
|
|
51980
|
+
kind: "updated",
|
|
51981
|
+
elementId: elementId2,
|
|
51982
|
+
data: mergedPreviewData(data, mergedState)
|
|
51983
|
+
};
|
|
51984
|
+
}
|
|
51985
|
+
}
|
|
51578
51986
|
const existingElements = this.#deps.canvasRepo.getElements(params.taskId);
|
|
51579
|
-
const
|
|
51580
|
-
|
|
51581
|
-
const
|
|
51582
|
-
|
|
51583
|
-
|
|
51584
|
-
|
|
51585
|
-
|
|
51586
|
-
|
|
51587
|
-
|
|
51588
|
-
|
|
51589
|
-
|
|
51590
|
-
|
|
51591
|
-
const existing = extractPreviewData(match2.data.data);
|
|
51592
|
-
if (existing?.projectRoot) {
|
|
51593
|
-
return { ...data, projectRoot: existing.projectRoot };
|
|
51594
|
-
}
|
|
51595
|
-
return data;
|
|
51596
|
-
})();
|
|
51597
|
-
const preservedLoroData = previewDataToLoroValue(preservedData);
|
|
51598
|
-
this.#deps.canvasRepo.updateElement(params.taskId, matchTreeId, { data: preservedLoroData });
|
|
51599
|
-
return { kind: "updated", elementId: `${match2.id}`, data: preservedData };
|
|
51987
|
+
const legacyMatch = store ? this.#findLegacyPreviewMatch(existingElements, params, data.ownerUserId) : null;
|
|
51988
|
+
if (legacyMatch && store) {
|
|
51989
|
+
const matchTreeId = legacyMatch.id;
|
|
51990
|
+
const elementId2 = `${matchTreeId}`;
|
|
51991
|
+
const existingState = store.getByElementId(params.taskId, elementId2);
|
|
51992
|
+
const mergedState = buildPreviewState(elementId2, params, data.ownerUserId, existingState);
|
|
51993
|
+
store.putState(params.taskId, mergedState);
|
|
51994
|
+
return {
|
|
51995
|
+
kind: "updated",
|
|
51996
|
+
elementId: elementId2,
|
|
51997
|
+
data: mergedPreviewData(data, mergedState)
|
|
51998
|
+
};
|
|
51600
51999
|
}
|
|
51601
52000
|
const positionable = existingElements.map((el) => ({
|
|
51602
52001
|
x: el.data.x,
|
|
@@ -51616,7 +52015,26 @@ var VisualizationFileWatcher = class {
|
|
|
51616
52015
|
zIndex: position.zIndex,
|
|
51617
52016
|
data: loroData
|
|
51618
52017
|
});
|
|
51619
|
-
|
|
52018
|
+
const elementId = `${nodeId}`;
|
|
52019
|
+
if (store) {
|
|
52020
|
+
const freshState = buildPreviewState(elementId, params, data.ownerUserId, void 0);
|
|
52021
|
+
store.putState(params.taskId, freshState);
|
|
52022
|
+
}
|
|
52023
|
+
return { kind: "created", elementId, data };
|
|
52024
|
+
}
|
|
52025
|
+
#findLegacyPreviewMatch(existingElements, params, ownerUserId) {
|
|
52026
|
+
const store = this.#deps.previewStateStore;
|
|
52027
|
+
if (!store) return null;
|
|
52028
|
+
for (const el of existingElements) {
|
|
52029
|
+
if (el.data.type !== "preview") continue;
|
|
52030
|
+
const elData = extractPreviewData(el.data.data);
|
|
52031
|
+
if (!elData || elData.ownerUserId !== ownerUserId) continue;
|
|
52032
|
+
const state = store.getByElementId(params.taskId, `${el.id}`);
|
|
52033
|
+
if (!state) continue;
|
|
52034
|
+
if (params.port !== void 0 && state.port === params.port) return el;
|
|
52035
|
+
if (params.url !== void 0 && state.url === params.url) return el;
|
|
52036
|
+
}
|
|
52037
|
+
return null;
|
|
51620
52038
|
}
|
|
51621
52039
|
async #createCanvasElement(effect) {
|
|
51622
52040
|
const existingElements = this.#deps.canvasRepo.getElements(effect.taskId);
|
|
@@ -51757,17 +52175,7 @@ var VisualizationRegistry = class {
|
|
|
51757
52175
|
* idempotency key derived from `{taskId, port || url}`.
|
|
51758
52176
|
*/
|
|
51759
52177
|
planPreviewElement(params) {
|
|
51760
|
-
|
|
51761
|
-
ownerUserId: params.ownerUserId
|
|
51762
|
-
};
|
|
51763
|
-
if (params.port !== void 0) data.port = params.port;
|
|
51764
|
-
if (params.url !== void 0) data.url = params.url;
|
|
51765
|
-
if (params.proxyPort !== void 0) data.proxyPort = params.proxyPort;
|
|
51766
|
-
if (params.initialPath !== void 0) data.initialPath = params.initialPath;
|
|
51767
|
-
if (typeof params.projectRoot === "string" && params.projectRoot.length > 0) {
|
|
51768
|
-
data.projectRoot = params.projectRoot;
|
|
51769
|
-
}
|
|
51770
|
-
return data;
|
|
52178
|
+
return { ownerUserId: params.ownerUserId };
|
|
51771
52179
|
}
|
|
51772
52180
|
/**
|
|
51773
52181
|
* Force-refresh the canvas element for a presented visualization, regardless
|
|
@@ -75398,7 +75806,7 @@ var ScheduleEvaluator = class {
|
|
|
75398
75806
|
};
|
|
75399
75807
|
|
|
75400
75808
|
// src/services/serve-factory-helpers.ts
|
|
75401
|
-
import { existsSync as existsSync6, readdirSync as
|
|
75809
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
|
|
75402
75810
|
import { homedir as homedir3 } from "os";
|
|
75403
75811
|
import { join as join34 } from "path";
|
|
75404
75812
|
|
|
@@ -77978,7 +78386,7 @@ function rehydrateVizRegistry(registry, vizWatcher, resolvedTaskId, vizDir, log)
|
|
|
77978
78386
|
const registryPath = join34(vizDir, resolvedTaskId, "registry.json");
|
|
77979
78387
|
if (!existsSync6(registryPath)) return Promise.resolve();
|
|
77980
78388
|
try {
|
|
77981
|
-
const raw =
|
|
78389
|
+
const raw = readFileSync7(registryPath, "utf-8");
|
|
77982
78390
|
const parsed = PersistedVisualizationRegistrySchema.safeParse(JSON.parse(raw));
|
|
77983
78391
|
if (parsed.success) {
|
|
77984
78392
|
const effects = registry.rehydrate(resolvedTaskId, parsed.data);
|
|
@@ -78003,7 +78411,7 @@ function rehydrateVizRegistry(registry, vizWatcher, resolvedTaskId, vizDir, log)
|
|
|
78003
78411
|
}
|
|
78004
78412
|
function prehydrateAllVizWatchers(vizDir, getOrCreateVizWatcher) {
|
|
78005
78413
|
try {
|
|
78006
|
-
for (const entry of
|
|
78414
|
+
for (const entry of readdirSync3(vizDir, { withFileTypes: true })) {
|
|
78007
78415
|
if (entry.isDirectory() && existsSync6(join34(vizDir, entry.name, "registry.json"))) {
|
|
78008
78416
|
getOrCreateVizWatcher(entry.name);
|
|
78009
78417
|
}
|
|
@@ -78729,7 +79137,7 @@ function resolveModelFamily(model) {
|
|
|
78729
79137
|
if (model.includes("haiku")) return "haiku";
|
|
78730
79138
|
return "sonnet";
|
|
78731
79139
|
}
|
|
78732
|
-
var CC_VERSION = "2.1.
|
|
79140
|
+
var CC_VERSION = "2.1.120";
|
|
78733
79141
|
var BILLING_HEADER_PREFIX = `x-anthropic-billing-header: cc_version=${CC_VERSION}; cc_entrypoint=sdk-ts;`;
|
|
78734
79142
|
function buildConversationSystemPrompt(model) {
|
|
78735
79143
|
const family = resolveModelFamily(model);
|
|
@@ -78825,7 +79233,7 @@ async function resolveDirectApiCredentials(method) {
|
|
|
78825
79233
|
}
|
|
78826
79234
|
|
|
78827
79235
|
// src/services/session/direct-api-subprocess.ts
|
|
78828
|
-
import { randomUUID as
|
|
79236
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
78829
79237
|
|
|
78830
79238
|
// ../../node_modules/.pnpm/@anthropic-ai+sdk@0.90.0_zod@4.3.6/node_modules/@anthropic-ai/sdk/internal/tslib.mjs
|
|
78831
79239
|
function __classPrivateFieldSet(receiver, state, value, kind, f2) {
|
|
@@ -84994,7 +85402,7 @@ var DirectApiSubprocess = class _DirectApiSubprocess {
|
|
|
84994
85402
|
const allToolNames = [...SERVER_SIDE_TOOLS.map((t) => t.name), ...tools.map((t) => t.name)];
|
|
84995
85403
|
onEvent({
|
|
84996
85404
|
type: "init_received",
|
|
84997
|
-
sessionId: `direct-api-${
|
|
85405
|
+
sessionId: `direct-api-${randomUUID6()}`,
|
|
84998
85406
|
metadata: {
|
|
84999
85407
|
tools: allToolNames,
|
|
85000
85408
|
skills: [],
|
|
@@ -85347,8 +85755,7 @@ async function handlePublish(ctx, input) {
|
|
|
85347
85755
|
previewPort: target.previewPort,
|
|
85348
85756
|
...target.canvasElementId !== void 0 && {
|
|
85349
85757
|
canvasElementId: target.canvasElementId
|
|
85350
|
-
}
|
|
85351
|
-
...target.projectRoot !== void 0 && { projectRoot: target.projectRoot }
|
|
85758
|
+
}
|
|
85352
85759
|
} : { kind: "canvasElementId", canvasElementId: target.canvasElementId };
|
|
85353
85760
|
const outcome = await runPublish(
|
|
85354
85761
|
{
|
|
@@ -85361,6 +85768,7 @@ async function handlePublish(ctx, input) {
|
|
|
85361
85768
|
taskId: ctx.taskId,
|
|
85362
85769
|
vizWatcher: ctx.vizWatcher,
|
|
85363
85770
|
publishedArtifactStore: ctx.publishedArtifactStore,
|
|
85771
|
+
previewStateStore: ctx.previewStateStore,
|
|
85364
85772
|
getDetectedPorts: ctx.getDetectedPorts
|
|
85365
85773
|
},
|
|
85366
85774
|
{ onProgress: () => {
|
|
@@ -85680,7 +86088,7 @@ function createShipyardResolver() {
|
|
|
85680
86088
|
|
|
85681
86089
|
// src/services/storage/annotation-store.ts
|
|
85682
86090
|
import { mkdir as mkdir11, readFile as readFile25, rename as rename10, writeFile as writeFile16 } from "fs/promises";
|
|
85683
|
-
import { dirname as
|
|
86091
|
+
import { dirname as dirname12, join as join35 } from "path";
|
|
85684
86092
|
var LegacyBaseFields = external_exports.object({
|
|
85685
86093
|
commentId: external_exports.string(),
|
|
85686
86094
|
body: external_exports.string(),
|
|
@@ -85732,7 +86140,7 @@ function buildAnnotationStore(dataDir) {
|
|
|
85732
86140
|
}
|
|
85733
86141
|
async function atomicWrite4(taskId, data) {
|
|
85734
86142
|
const fp = filePath(taskId);
|
|
85735
|
-
await mkdir11(
|
|
86143
|
+
await mkdir11(dirname12(fp), { recursive: true });
|
|
85736
86144
|
const tmpPath = `${fp}.tmp`;
|
|
85737
86145
|
await writeFile16(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
85738
86146
|
await rename10(tmpPath, fp);
|
|
@@ -86102,7 +86510,7 @@ async function seedBuiltInTemplates(store, builtIns, dismissed) {
|
|
|
86102
86510
|
|
|
86103
86511
|
// src/services/storage/credentials-vault-store.ts
|
|
86104
86512
|
import { mkdir as mkdir13, readFile as readFile27, rename as rename12, writeFile as writeFile18 } from "fs/promises";
|
|
86105
|
-
import { dirname as
|
|
86513
|
+
import { dirname as dirname13 } from "path";
|
|
86106
86514
|
function buildCredentialsVaultStore(filePath) {
|
|
86107
86515
|
let cache2 = null;
|
|
86108
86516
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -86116,7 +86524,7 @@ function buildCredentialsVaultStore(filePath) {
|
|
|
86116
86524
|
}
|
|
86117
86525
|
}
|
|
86118
86526
|
async function ensureDir() {
|
|
86119
|
-
await mkdir13(
|
|
86527
|
+
await mkdir13(dirname13(filePath), { recursive: true });
|
|
86120
86528
|
}
|
|
86121
86529
|
async function readStore() {
|
|
86122
86530
|
if (cache2) return cache2;
|
|
@@ -86646,9 +87054,9 @@ function buildObservableConversationStore(inner) {
|
|
|
86646
87054
|
}
|
|
86647
87055
|
|
|
86648
87056
|
// src/services/storage/projects-store.ts
|
|
86649
|
-
import { randomUUID as
|
|
87057
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
86650
87058
|
import { mkdir as mkdir15, readFile as readFile29, rename as rename14, writeFile as writeFile20 } from "fs/promises";
|
|
86651
|
-
import { dirname as
|
|
87059
|
+
import { dirname as dirname14 } from "path";
|
|
86652
87060
|
var ProjectsArraySchema = external_exports.array(ProjectSchema);
|
|
86653
87061
|
function buildProjectsStore(filePath) {
|
|
86654
87062
|
let cache2 = null;
|
|
@@ -86663,7 +87071,7 @@ function buildProjectsStore(filePath) {
|
|
|
86663
87071
|
}
|
|
86664
87072
|
}
|
|
86665
87073
|
async function ensureDir() {
|
|
86666
|
-
await mkdir15(
|
|
87074
|
+
await mkdir15(dirname14(filePath), { recursive: true });
|
|
86667
87075
|
}
|
|
86668
87076
|
async function readStore() {
|
|
86669
87077
|
if (cache2) return cache2;
|
|
@@ -86723,7 +87131,7 @@ function buildProjectsStore(filePath) {
|
|
|
86723
87131
|
const current2 = await readStore();
|
|
86724
87132
|
const nextOrder = current2.length === 0 ? 0 : Math.max(...current2.map((p2) => p2.order)) + 1;
|
|
86725
87133
|
const project = {
|
|
86726
|
-
id:
|
|
87134
|
+
id: randomUUID7(),
|
|
86727
87135
|
name,
|
|
86728
87136
|
order: nextOrder,
|
|
86729
87137
|
createdAt: Date.now(),
|
|
@@ -86787,7 +87195,7 @@ function buildProjectsStore(filePath) {
|
|
|
86787
87195
|
|
|
86788
87196
|
// src/services/storage/rate-limit-store.ts
|
|
86789
87197
|
import { mkdir as mkdir16, readFile as readFile30, rename as rename15, writeFile as writeFile21 } from "fs/promises";
|
|
86790
|
-
import { dirname as
|
|
87198
|
+
import { dirname as dirname15, join as join38 } from "path";
|
|
86791
87199
|
var RATE_LIMIT_STORE_VERSION = 2;
|
|
86792
87200
|
var RateLimitRecordSchema = external_exports.object({
|
|
86793
87201
|
info: RateLimitInfoSchema,
|
|
@@ -86969,7 +87377,7 @@ function migrateV1toV2(v1) {
|
|
|
86969
87377
|
return { schemaVersion: RATE_LIMIT_STORE_VERSION, records };
|
|
86970
87378
|
}
|
|
86971
87379
|
async function atomicWrite3(filePath, data) {
|
|
86972
|
-
await mkdir16(
|
|
87380
|
+
await mkdir16(dirname15(filePath), { recursive: true });
|
|
86973
87381
|
const tmpPath = `${filePath}.tmp`;
|
|
86974
87382
|
await writeFile21(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
86975
87383
|
await rename15(tmpPath, filePath);
|
|
@@ -86980,7 +87388,7 @@ import { join as join39 } from "path";
|
|
|
86980
87388
|
|
|
86981
87389
|
// src/services/storage/json-document-store.ts
|
|
86982
87390
|
import { mkdir as mkdir17, readFile as readFile31, rename as rename16, writeFile as writeFile22 } from "fs/promises";
|
|
86983
|
-
import { dirname as
|
|
87391
|
+
import { dirname as dirname16 } from "path";
|
|
86984
87392
|
function buildJsonDocumentStore(opts) {
|
|
86985
87393
|
const { filePath, recordSchema, currentVersion, migrate } = opts;
|
|
86986
87394
|
let cache2 = null;
|
|
@@ -86995,7 +87403,7 @@ function buildJsonDocumentStore(opts) {
|
|
|
86995
87403
|
}
|
|
86996
87404
|
}
|
|
86997
87405
|
async function ensureDir() {
|
|
86998
|
-
await mkdir17(
|
|
87406
|
+
await mkdir17(dirname16(filePath), { recursive: true });
|
|
86999
87407
|
}
|
|
87000
87408
|
async function readStore() {
|
|
87001
87409
|
if (cache2) return cache2;
|
|
@@ -87231,7 +87639,7 @@ function buildTemplateStore(dataDir) {
|
|
|
87231
87639
|
|
|
87232
87640
|
// src/services/storage/user-settings-store.ts
|
|
87233
87641
|
import { mkdir as mkdir19, readFile as readFile33, rename as rename18, writeFile as writeFile24 } from "fs/promises";
|
|
87234
|
-
import { dirname as
|
|
87642
|
+
import { dirname as dirname17 } from "path";
|
|
87235
87643
|
function buildUserSettingsStore(filePath) {
|
|
87236
87644
|
let cache2 = null;
|
|
87237
87645
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -87245,7 +87653,7 @@ function buildUserSettingsStore(filePath) {
|
|
|
87245
87653
|
}
|
|
87246
87654
|
}
|
|
87247
87655
|
async function ensureDir() {
|
|
87248
|
-
await mkdir19(
|
|
87656
|
+
await mkdir19(dirname17(filePath), { recursive: true });
|
|
87249
87657
|
}
|
|
87250
87658
|
async function readStore() {
|
|
87251
87659
|
if (cache2) return cache2;
|
|
@@ -87336,7 +87744,7 @@ function buildUserSettingsStore(filePath) {
|
|
|
87336
87744
|
}
|
|
87337
87745
|
|
|
87338
87746
|
// src/services/task/orchestrator/task.ts
|
|
87339
|
-
import { randomUUID as
|
|
87747
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
87340
87748
|
import { join as join50 } from "path";
|
|
87341
87749
|
|
|
87342
87750
|
// src/services/event-batching.ts
|
|
@@ -90190,13 +90598,13 @@ function skipForMainChannel(content) {
|
|
|
90190
90598
|
}
|
|
90191
90599
|
|
|
90192
90600
|
// src/services/plan/plan-handler.ts
|
|
90193
|
-
import { existsSync as existsSync8, readdirSync as
|
|
90601
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
90194
90602
|
import { readFile as readFile35 } from "fs/promises";
|
|
90195
90603
|
import { homedir as homedir5 } from "os";
|
|
90196
90604
|
import { join as join44 } from "path";
|
|
90197
90605
|
|
|
90198
90606
|
// src/services/plan/plan-file-bridge.ts
|
|
90199
|
-
import { createHash as createHash3, randomUUID as
|
|
90607
|
+
import { createHash as createHash3, randomUUID as randomUUID8 } from "crypto";
|
|
90200
90608
|
import { existsSync as existsSync7, watch as watch4 } from "fs";
|
|
90201
90609
|
import { readFile as readFile34, rename as rename19, writeFile as writeFile25 } from "fs/promises";
|
|
90202
90610
|
import { homedir as homedir4 } from "os";
|
|
@@ -90461,7 +90869,7 @@ var PlanFileBridge = class _PlanFileBridge {
|
|
|
90461
90869
|
const content = this.#config.planRepo.getContent(this.#config.taskId);
|
|
90462
90870
|
const hash = contentHash(content);
|
|
90463
90871
|
if (hash === this.#lastWrittenHash) return;
|
|
90464
|
-
const tmpPath = `${this.#filePath}.${
|
|
90872
|
+
const tmpPath = `${this.#filePath}.${randomUUID8()}.tmp`;
|
|
90465
90873
|
await writeFile25(tmpPath, content, "utf-8");
|
|
90466
90874
|
await rename19(tmpPath, this.#filePath);
|
|
90467
90875
|
this.#lastWrittenHash = hash;
|
|
@@ -91214,7 +91622,7 @@ var PlanHandler = class {
|
|
|
91214
91622
|
const plansDir = join44(homedir5(), ".claude", "plans");
|
|
91215
91623
|
if (!existsSync8(plansDir)) return null;
|
|
91216
91624
|
try {
|
|
91217
|
-
const files =
|
|
91625
|
+
const files = readdirSync4(plansDir).filter((f2) => f2.endsWith(".md")).map((f2) => ({
|
|
91218
91626
|
path: join44(plansDir, f2),
|
|
91219
91627
|
mtime: statSync2(join44(plansDir, f2)).mtimeMs
|
|
91220
91628
|
})).sort((a, b2) => b2.mtime - a.mtime);
|
|
@@ -91833,14 +92241,14 @@ function planPostCompactPending(input) {
|
|
|
91833
92241
|
}
|
|
91834
92242
|
|
|
91835
92243
|
// src/services/task/compaction-snapshot.ts
|
|
91836
|
-
import { randomUUID as
|
|
92244
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
91837
92245
|
import { homedir as homedir6 } from "os";
|
|
91838
92246
|
import { join as pathJoin } from "path";
|
|
91839
92247
|
function resolveArchiveAbsolutePath(channelId) {
|
|
91840
92248
|
return pathJoin(homedir6(), ".shipyard", "data", "channels", `${channelId}.jsonl`);
|
|
91841
92249
|
}
|
|
91842
92250
|
function planCompactionSnapshot(input) {
|
|
91843
|
-
const threadId =
|
|
92251
|
+
const threadId = randomUUID9();
|
|
91844
92252
|
const channelId = buildThreadChannelId(input.taskId, threadId);
|
|
91845
92253
|
const compactionCount = input.messages.filter(
|
|
91846
92254
|
(m2) => m2.content.some((b2) => b2.type === "compaction_boundary")
|
|
@@ -91877,7 +92285,7 @@ function planCompactionSnapshot(input) {
|
|
|
91877
92285
|
stats
|
|
91878
92286
|
};
|
|
91879
92287
|
const boundaryMessage = {
|
|
91880
|
-
messageId:
|
|
92288
|
+
messageId: randomUUID9(),
|
|
91881
92289
|
channelId: input.channelId,
|
|
91882
92290
|
participantId: input.humanParticipantId,
|
|
91883
92291
|
senderKind: "human",
|
|
@@ -92757,7 +93165,7 @@ var RewindCheckpointHandler = class {
|
|
|
92757
93165
|
|
|
92758
93166
|
// src/services/task/side-thread-registry.ts
|
|
92759
93167
|
import { mkdir as mkdir20, readFile as readFile36, rename as rename20, writeFile as writeFile26 } from "fs/promises";
|
|
92760
|
-
import { dirname as
|
|
93168
|
+
import { dirname as dirname18, join as join45 } from "path";
|
|
92761
93169
|
var ThreadFileSchema = external_exports.object({
|
|
92762
93170
|
threads: external_exports.record(external_exports.string(), ThreadMetadataSchema)
|
|
92763
93171
|
});
|
|
@@ -93118,7 +93526,7 @@ var SideThreadRegistry = class {
|
|
|
93118
93526
|
threads[threadId] = entry.metadata;
|
|
93119
93527
|
}
|
|
93120
93528
|
const filePath = this.#filePath();
|
|
93121
|
-
await mkdir20(
|
|
93529
|
+
await mkdir20(dirname18(filePath), { recursive: true });
|
|
93122
93530
|
const tmpPath = `${filePath}.tmp`;
|
|
93123
93531
|
await writeFile26(tmpPath, JSON.stringify({ threads }, null, 2), "utf-8");
|
|
93124
93532
|
await rename20(tmpPath, filePath);
|
|
@@ -93533,7 +93941,7 @@ async function resolveResources(ctx, content, history2, excludeUris) {
|
|
|
93533
93941
|
import { watch as watch5 } from "fs";
|
|
93534
93942
|
import { readdir as readdir10, readFile as readFile37 } from "fs/promises";
|
|
93535
93943
|
import { homedir as homedir7 } from "os";
|
|
93536
|
-
import { basename as basename5, dirname as
|
|
93944
|
+
import { basename as basename5, dirname as dirname19, join as join46 } from "path";
|
|
93537
93945
|
var VALID_STATUSES2 = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
|
|
93538
93946
|
function sanitize(id) {
|
|
93539
93947
|
return id.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
@@ -93586,7 +93994,7 @@ function createCCTaskFileWatcher(listId, log) {
|
|
|
93586
93994
|
let debounceTimer = null;
|
|
93587
93995
|
const DEBOUNCE_MS4 = 200;
|
|
93588
93996
|
const targetDirName = basename5(dir);
|
|
93589
|
-
const parentDir =
|
|
93997
|
+
const parentDir = dirname19(dir);
|
|
93590
93998
|
function scheduleRead() {
|
|
93591
93999
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
93592
94000
|
debounceTimer = setTimeout(() => {
|
|
@@ -96285,7 +96693,7 @@ var Task = class {
|
|
|
96285
96693
|
instructions
|
|
96286
96694
|
});
|
|
96287
96695
|
const archiveMessages = plan.messagesToCopy.map((msg) => ({
|
|
96288
|
-
messageId:
|
|
96696
|
+
messageId: randomUUID10(),
|
|
96289
96697
|
channelId: plan.threadMetadata.channelId,
|
|
96290
96698
|
participantId: msg.participantId,
|
|
96291
96699
|
senderKind: msg.senderKind,
|
|
@@ -100444,7 +100852,7 @@ function buildThemeStore(dataDir) {
|
|
|
100444
100852
|
// src/services/themes/vscode-scanner.ts
|
|
100445
100853
|
import { readdir as readdir13, readFile as readFile39, stat as stat11 } from "fs/promises";
|
|
100446
100854
|
import { homedir as homedir9 } from "os";
|
|
100447
|
-
import { dirname as
|
|
100855
|
+
import { dirname as dirname20, join as join53, normalize as normalize5, resolve as resolve2 } from "path";
|
|
100448
100856
|
var VSCodeThemeEntrySchema2 = external_exports.object({
|
|
100449
100857
|
extensionId: external_exports.string(),
|
|
100450
100858
|
name: external_exports.string(),
|
|
@@ -100599,7 +101007,7 @@ async function readVSCodeThemeWithIncludes(absolutePath) {
|
|
|
100599
101007
|
const obj = await readObject(canonical);
|
|
100600
101008
|
const include = typeof obj.include === "string" ? obj.include : null;
|
|
100601
101009
|
if (!include) return obj;
|
|
100602
|
-
const basePath = resolve2(
|
|
101010
|
+
const basePath = resolve2(dirname20(canonical), include);
|
|
100603
101011
|
const base3 = await walk(basePath, depth + 1);
|
|
100604
101012
|
return mergeThemeObjects(base3, obj);
|
|
100605
101013
|
};
|
|
@@ -100912,7 +101320,8 @@ async function createDaemon(deps) {
|
|
|
100912
101320
|
const watcher = new VisualizationFileWatcher(registry, {
|
|
100913
101321
|
canvasRepo,
|
|
100914
101322
|
vizDir,
|
|
100915
|
-
log: deps.log
|
|
101323
|
+
log: deps.log,
|
|
101324
|
+
previewStateStore: deps.previewStateStore ?? null
|
|
100916
101325
|
});
|
|
100917
101326
|
watcher.setSendControlMessage((msg) => taskManager.broadcastControl(msg));
|
|
100918
101327
|
watcher.setRehydrationPromise(
|
|
@@ -100937,26 +101346,25 @@ async function createDaemon(deps) {
|
|
|
100937
101346
|
}
|
|
100938
101347
|
const trackedPreviewPorts = /* @__PURE__ */ new Map();
|
|
100939
101348
|
const previewLifecycleSubs = /* @__PURE__ */ new Map();
|
|
100940
|
-
function
|
|
100941
|
-
if (!isRecord4(dataField)) return
|
|
100942
|
-
|
|
100943
|
-
const port = dataField.port;
|
|
100944
|
-
return typeof port === "number" ? port : void 0;
|
|
101349
|
+
function isLocalPreviewElement(dataField, selfUserId) {
|
|
101350
|
+
if (!isRecord4(dataField)) return false;
|
|
101351
|
+
return dataField.ownerUserId === selfUserId;
|
|
100945
101352
|
}
|
|
100946
101353
|
function collectLocalPreviewPorts(taskId) {
|
|
100947
101354
|
const current2 = /* @__PURE__ */ new Map();
|
|
101355
|
+
const store2 = deps.previewStateStore ?? null;
|
|
101356
|
+
if (!store2) return current2;
|
|
100948
101357
|
for (const el of canvasRepo.getElements(taskId)) {
|
|
100949
101358
|
if (el.data.type !== "preview") continue;
|
|
100950
|
-
|
|
100951
|
-
|
|
101359
|
+
if (!isLocalPreviewElement(el.data.data, deps.auth.userId)) continue;
|
|
101360
|
+
const state = store2.getByElementId(taskId, String(el.id));
|
|
101361
|
+
const port = state?.port;
|
|
101362
|
+
if (typeof port === "number") current2.set(String(el.id), port);
|
|
100952
101363
|
}
|
|
100953
101364
|
return current2;
|
|
100954
101365
|
}
|
|
100955
|
-
function
|
|
100956
|
-
const
|
|
100957
|
-
const tracked = trackedPreviewPorts.get(taskId) ?? /* @__PURE__ */ new Map();
|
|
100958
|
-
trackedPreviewPorts.set(taskId, current2);
|
|
100959
|
-
for (const [elementId, port] of tracked) {
|
|
101366
|
+
function releaseRemovedProxyRefs(previous, current2) {
|
|
101367
|
+
for (const [elementId, port] of previous) {
|
|
100960
101368
|
const stillPresent = current2.get(elementId);
|
|
100961
101369
|
if (stillPresent === void 0 || stillPresent !== port) {
|
|
100962
101370
|
deps.previewProxy.release(port).catch(() => {
|
|
@@ -100964,6 +101372,22 @@ async function createDaemon(deps) {
|
|
|
100964
101372
|
}
|
|
100965
101373
|
}
|
|
100966
101374
|
}
|
|
101375
|
+
function sweepOrphanedPreviewState(taskId, liveElementIds) {
|
|
101376
|
+
const store2 = deps.previewStateStore ?? null;
|
|
101377
|
+
if (!store2) return;
|
|
101378
|
+
for (const state of store2.listByTask(taskId)) {
|
|
101379
|
+
if (liveElementIds.has(state.elementId)) continue;
|
|
101380
|
+
const el = canvasRepo.getElements(taskId).find((e) => String(e.id) === state.elementId);
|
|
101381
|
+
if (!el) store2.deleteByElementId(taskId, state.elementId);
|
|
101382
|
+
}
|
|
101383
|
+
}
|
|
101384
|
+
function reconcilePreviewRefs(taskId) {
|
|
101385
|
+
const current2 = collectLocalPreviewPorts(taskId);
|
|
101386
|
+
const tracked = trackedPreviewPorts.get(taskId) ?? /* @__PURE__ */ new Map();
|
|
101387
|
+
trackedPreviewPorts.set(taskId, current2);
|
|
101388
|
+
releaseRemovedProxyRefs(tracked, current2);
|
|
101389
|
+
sweepOrphanedPreviewState(taskId, new Set(current2.keys()));
|
|
101390
|
+
}
|
|
100967
101391
|
function ensurePreviewLifecycleTracking(taskId) {
|
|
100968
101392
|
if (previewLifecycleSubs.has(taskId)) return;
|
|
100969
101393
|
const unsub = canvasRepo.subscribe(
|
|
@@ -101003,6 +101427,7 @@ async function createDaemon(deps) {
|
|
|
101003
101427
|
getAuthToken: () => deps.auth.token,
|
|
101004
101428
|
projectRoot: cwd ?? deps.workspaceRoot,
|
|
101005
101429
|
publishedArtifactStore: deps.publishedArtifactStore ?? null,
|
|
101430
|
+
previewStateStore: deps.previewStateStore ?? null,
|
|
101006
101431
|
getDetectedPorts: deps.getDetectedPorts ?? (() => [])
|
|
101007
101432
|
});
|
|
101008
101433
|
const options = buildSpawnOptions({
|
|
@@ -101042,6 +101467,7 @@ async function createDaemon(deps) {
|
|
|
101042
101467
|
getAuthToken: () => deps.auth.token,
|
|
101043
101468
|
projectRoot,
|
|
101044
101469
|
publishedArtifactStore: deps.publishedArtifactStore ?? null,
|
|
101470
|
+
previewStateStore: deps.previewStateStore ?? null,
|
|
101045
101471
|
getDetectedPorts: deps.getDetectedPorts ?? (() => [])
|
|
101046
101472
|
});
|
|
101047
101473
|
deps.log({ event: "conversation_backend", backend: "direct-api", model, taskId });
|
|
@@ -101334,6 +101760,9 @@ async function createDaemon(deps) {
|
|
|
101334
101760
|
await sweepStaleTasks(taskStateStore, taskManager, deps.log);
|
|
101335
101761
|
await migratePinnedToFavoriteTasks(taskStateStore, userSettingsStore, deps.log);
|
|
101336
101762
|
await rehydrateCollabQueues(collabQueuePersistence, taskManager, deps.log);
|
|
101763
|
+
if (deps.previewStateStore) {
|
|
101764
|
+
await migrateLegacyPreviewData(canvasRepo, deps.previewStateStore, taskStateStore, deps.log);
|
|
101765
|
+
}
|
|
101337
101766
|
const warmedPreviewTasks = await rehydrateCanvasPreviews(
|
|
101338
101767
|
canvasRepo,
|
|
101339
101768
|
deps.previewProxy,
|
|
@@ -101931,6 +102360,14 @@ function filterOutboundForCollab(msg, collabTaskId, getTaskSync) {
|
|
|
101931
102360
|
case "background_agent_update":
|
|
101932
102361
|
case "viz_content":
|
|
101933
102362
|
case "viz_content_batch":
|
|
102363
|
+
/**
|
|
102364
|
+
* published_artifacts_state and preview_elements_state are per-task
|
|
102365
|
+
* but visible to all collaborators so the chip + iframe on each
|
|
102366
|
+
* canvas element reflect the same reality for everyone. Same rule
|
|
102367
|
+
* as the other task-scoped messages: match taskId or drop.
|
|
102368
|
+
*/
|
|
102369
|
+
case "published_artifacts_state":
|
|
102370
|
+
case "preview_elements_state":
|
|
101934
102371
|
if ("taskId" in msg && msg.taskId !== collabTaskId) return null;
|
|
101935
102372
|
return msg;
|
|
101936
102373
|
/** Preview: collab peers need port detection and preview targets */
|
|
@@ -101956,13 +102393,6 @@ function filterOutboundForCollab(msg, collabTaskId, getTaskSync) {
|
|
|
101956
102393
|
case "publish_progress":
|
|
101957
102394
|
case "publish_result":
|
|
101958
102395
|
return null;
|
|
101959
|
-
/**
|
|
101960
|
-
* published_artifacts_state is per-task but visible to all collaborators
|
|
101961
|
-
* so the chip on each element reflects the same reality for everyone.
|
|
101962
|
-
* Filter by taskId so a peer only sees state for tasks they participate in.
|
|
101963
|
-
*/
|
|
101964
|
-
case "published_artifacts_state":
|
|
101965
|
-
return msg.taskId === collabTaskId ? msg : null;
|
|
101966
102396
|
/** Exhaustiveness check: compile error if a new type is unhandled */
|
|
101967
102397
|
default: {
|
|
101968
102398
|
const _exhaustive = msg;
|
|
@@ -102516,7 +102946,7 @@ import { readdir as readdir14 } from "fs/promises";
|
|
|
102516
102946
|
import { execFile as execFile8, spawn as spawn5 } from "child_process";
|
|
102517
102947
|
import { closeSync, openSync } from "fs";
|
|
102518
102948
|
import { access as access3, chmod as chmod2, constants, mkdir as mkdir24, writeFile as writeFile30 } from "fs/promises";
|
|
102519
|
-
import { dirname as
|
|
102949
|
+
import { dirname as dirname21, isAbsolute as isAbsolute2, join as join55 } from "path";
|
|
102520
102950
|
var GIT_TIMEOUT_MS = 3e4;
|
|
102521
102951
|
var MAX_BUFFER = 10 * 1024 * 1024;
|
|
102522
102952
|
var BASE_REF_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/;
|
|
@@ -102779,7 +103209,7 @@ async function removeWorktree(worktreePath) {
|
|
|
102779
103209
|
if (!worktreePath.includes("-wt/")) {
|
|
102780
103210
|
throw new Error("worktreePath must be under a -wt/ parent directory");
|
|
102781
103211
|
}
|
|
102782
|
-
await runGit(["worktree", "remove", worktreePath],
|
|
103212
|
+
await runGit(["worktree", "remove", worktreePath], dirname21(worktreePath));
|
|
102783
103213
|
}
|
|
102784
103214
|
|
|
102785
103215
|
// src/services/channels/control-channel-infra-handlers.ts
|
|
@@ -103274,7 +103704,7 @@ function runPluginOp(pluginName, marketplace, action, ctx) {
|
|
|
103274
103704
|
}
|
|
103275
103705
|
|
|
103276
103706
|
// src/services/channels/read-recent-logs.ts
|
|
103277
|
-
import { createReadStream, readdirSync as
|
|
103707
|
+
import { createReadStream, readdirSync as readdirSync5 } from "fs";
|
|
103278
103708
|
import { join as join56 } from "path";
|
|
103279
103709
|
import { createInterface } from "readline";
|
|
103280
103710
|
var MAX_BYTES = 5e4;
|
|
@@ -103332,7 +103762,7 @@ async function readRecentLogs(logDir, windowMinutes, maxBytes = MAX_BYTES) {
|
|
|
103332
103762
|
function discoverLogFiles(logDir) {
|
|
103333
103763
|
let entries;
|
|
103334
103764
|
try {
|
|
103335
|
-
entries =
|
|
103765
|
+
entries = readdirSync5(logDir);
|
|
103336
103766
|
} catch {
|
|
103337
103767
|
return [];
|
|
103338
103768
|
}
|
|
@@ -104919,6 +105349,7 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
104919
105349
|
}
|
|
104920
105350
|
};
|
|
104921
105351
|
const publishedArtifactStore = deps.publishedArtifactStore;
|
|
105352
|
+
const previewStateStore = deps.previewStateStore;
|
|
104922
105353
|
const getDetectedPorts = deps.getDetectedPorts;
|
|
104923
105354
|
handlePublishRequest({
|
|
104924
105355
|
correlationId,
|
|
@@ -104931,6 +105362,7 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
104931
105362
|
projectRoot: wsRoot,
|
|
104932
105363
|
vizWatcher,
|
|
104933
105364
|
publishedArtifactStore,
|
|
105365
|
+
previewStateStore,
|
|
104934
105366
|
getDetectedPorts,
|
|
104935
105367
|
sendControl: wrappedSendControl,
|
|
104936
105368
|
log: (entry) => {
|
|
@@ -105150,6 +105582,24 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
105150
105582
|
});
|
|
105151
105583
|
});
|
|
105152
105584
|
}
|
|
105585
|
+
if (deps?.previewStateStore) {
|
|
105586
|
+
const previewStore = deps.previewStateStore;
|
|
105587
|
+
daemon.taskStateStore.listTasks().then((tasks) => {
|
|
105588
|
+
for (const taskId of Object.keys(tasks)) {
|
|
105589
|
+
const entries = previewStore.listByTask(taskId);
|
|
105590
|
+
controlHandler.sendControl({
|
|
105591
|
+
type: "preview_elements_state",
|
|
105592
|
+
taskId,
|
|
105593
|
+
entries
|
|
105594
|
+
});
|
|
105595
|
+
}
|
|
105596
|
+
}).catch((err) => {
|
|
105597
|
+
logAdapter({
|
|
105598
|
+
event: "preview_elements_initial_push_failed",
|
|
105599
|
+
error: err instanceof Error ? err.message : String(err)
|
|
105600
|
+
});
|
|
105601
|
+
});
|
|
105602
|
+
}
|
|
105153
105603
|
const userSettingsUnsub = daemon.userSettingsStore.subscribe((settings) => {
|
|
105154
105604
|
controlHandler.sendControl({ type: "user_settings_updated", settings });
|
|
105155
105605
|
});
|
|
@@ -105684,7 +106134,7 @@ import { promisify as promisify7 } from "util";
|
|
|
105684
106134
|
|
|
105685
106135
|
// src/shared/file-io-path-safety.ts
|
|
105686
106136
|
import { realpath } from "fs/promises";
|
|
105687
|
-
import { basename as basename6, dirname as
|
|
106137
|
+
import { basename as basename6, dirname as dirname22, join as join57, normalize as normalize6 } from "path";
|
|
105688
106138
|
async function safeAbsolutePath(userPath, isHidden2, allowedHiddenNames) {
|
|
105689
106139
|
const normalized = prepareAbsolutePath(userPath, isHidden2, allowedHiddenNames);
|
|
105690
106140
|
if (normalized === null) return null;
|
|
@@ -105706,13 +106156,13 @@ async function canonicalizeOrRecombine(path2) {
|
|
|
105706
106156
|
try {
|
|
105707
106157
|
return await realpath(path2);
|
|
105708
106158
|
} catch (err) {
|
|
105709
|
-
if (!
|
|
105710
|
-
const parentCanonical = await realpath(
|
|
106159
|
+
if (!isEnoent4(err)) return null;
|
|
106160
|
+
const parentCanonical = await realpath(dirname22(path2)).catch(() => null);
|
|
105711
106161
|
if (parentCanonical === null) return null;
|
|
105712
106162
|
return join57(parentCanonical, basename6(path2));
|
|
105713
106163
|
}
|
|
105714
106164
|
}
|
|
105715
|
-
function
|
|
106165
|
+
function isEnoent4(err) {
|
|
105716
106166
|
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
105717
106167
|
}
|
|
105718
106168
|
function checkSegmentsAllowed(path2, isHidden2, allowedHiddenNames) {
|
|
@@ -107022,13 +107472,13 @@ function wireThreadErrorFallback(daemon, dc, taskId, threadId, channelId, log) {
|
|
|
107022
107472
|
// src/shared/pty-manager.ts
|
|
107023
107473
|
import { accessSync, chmodSync, constants as constants2 } from "fs";
|
|
107024
107474
|
import { createRequire as createRequire4 } from "module";
|
|
107025
|
-
import { dirname as
|
|
107475
|
+
import { dirname as dirname23, resolve as resolve4 } from "path";
|
|
107026
107476
|
import * as pty from "node-pty";
|
|
107027
107477
|
function ensureSpawnHelperExecutable() {
|
|
107028
107478
|
if (globalThis.process.platform === "win32") return;
|
|
107029
107479
|
try {
|
|
107030
107480
|
const req = createRequire4(import.meta.url);
|
|
107031
|
-
const nodePtyDir =
|
|
107481
|
+
const nodePtyDir = dirname23(req.resolve("node-pty/package.json"));
|
|
107032
107482
|
const spawnHelper = resolve4(
|
|
107033
107483
|
nodePtyDir,
|
|
107034
107484
|
"prebuilds",
|
|
@@ -107487,8 +107937,9 @@ function buildCollabRoomManager(deps) {
|
|
|
107487
107937
|
configStore: pluginConfigStore,
|
|
107488
107938
|
previewProxy,
|
|
107489
107939
|
presencePool: presencePoolRef,
|
|
107490
|
-
/** Collab peers cannot publish — null
|
|
107940
|
+
/** Collab peers cannot publish — null stores enforce read-only access. */
|
|
107491
107941
|
publishedArtifactStore: null,
|
|
107942
|
+
previewStateStore: null,
|
|
107492
107943
|
getDetectedPorts: () => []
|
|
107493
107944
|
});
|
|
107494
107945
|
if (loadedPluginsRef.current.length > 0) {
|
|
@@ -107770,7 +108221,7 @@ function buildCollabRoomManager(deps) {
|
|
|
107770
108221
|
import { execSync } from "child_process";
|
|
107771
108222
|
import { existsSync as existsSync9 } from "fs";
|
|
107772
108223
|
import { createRequire as createRequire5 } from "module";
|
|
107773
|
-
import { dirname as
|
|
108224
|
+
import { dirname as dirname24, join as join59 } from "path";
|
|
107774
108225
|
|
|
107775
108226
|
// src/services/bootstrap/self-update.ts
|
|
107776
108227
|
import { execFile as execFile11, spawn as spawn9 } from "child_process";
|
|
@@ -108433,7 +108884,7 @@ function resolveClaudeCodePath(log) {
|
|
|
108433
108884
|
try {
|
|
108434
108885
|
const req = createRequire5(import.meta.url);
|
|
108435
108886
|
const sdkMain = req.resolve("@anthropic-ai/claude-agent-sdk");
|
|
108436
|
-
const p2 = join59(
|
|
108887
|
+
const p2 = join59(dirname24(sdkMain), "cli.js");
|
|
108437
108888
|
if (existsSync9(p2)) return ok("sdk_bundled", p2);
|
|
108438
108889
|
} catch {
|
|
108439
108890
|
}
|
|
@@ -108697,7 +109148,8 @@ function buildSharedChannelCallbacks(deps) {
|
|
|
108697
109148
|
terminalPtys,
|
|
108698
109149
|
signalingHandle,
|
|
108699
109150
|
sessionServerUrl,
|
|
108700
|
-
publishedArtifactStore
|
|
109151
|
+
publishedArtifactStore,
|
|
109152
|
+
previewStateStore
|
|
108701
109153
|
} = deps;
|
|
108702
109154
|
return {
|
|
108703
109155
|
onPeerDataChannel: (machineId) => {
|
|
@@ -108720,6 +109172,7 @@ function buildSharedChannelCallbacks(deps) {
|
|
|
108720
109172
|
sessionServerUrl,
|
|
108721
109173
|
getAuthToken: () => auth3.token,
|
|
108722
109174
|
publishedArtifactStore,
|
|
109175
|
+
previewStateStore,
|
|
108723
109176
|
getDetectedPorts: () => detectedPortsRef.current
|
|
108724
109177
|
});
|
|
108725
109178
|
portDetectorRef.current?.resend();
|
|
@@ -109133,6 +109586,10 @@ async function serve(options = {}) {
|
|
|
109133
109586
|
rootDir: join62(dataDir, "published"),
|
|
109134
109587
|
logger: log
|
|
109135
109588
|
});
|
|
109589
|
+
const previewStateStore = createPreviewStateStore({
|
|
109590
|
+
rootDir: join62(dataDir, "preview-state"),
|
|
109591
|
+
logger: log
|
|
109592
|
+
});
|
|
109136
109593
|
const daemon = await createDaemon({
|
|
109137
109594
|
shipyardHome,
|
|
109138
109595
|
dataDir,
|
|
@@ -109152,10 +109609,12 @@ async function serve(options = {}) {
|
|
|
109152
109609
|
previewProxy,
|
|
109153
109610
|
sessionServerUrl,
|
|
109154
109611
|
publishedArtifactStore,
|
|
109612
|
+
previewStateStore,
|
|
109155
109613
|
getDetectedPorts: () => detectedPortsRef.current
|
|
109156
109614
|
});
|
|
109157
109615
|
daemon.healthMetrics.start();
|
|
109158
109616
|
publishedArtifactStore.setSendControlMessage((msg) => daemon.taskManager.broadcastControl(msg));
|
|
109617
|
+
previewStateStore.setSendControlMessage((msg) => daemon.taskManager.broadcastControl(msg));
|
|
109159
109618
|
const pluginsDir = join62(shipyardHome, "plugins");
|
|
109160
109619
|
await mkdir28(pluginsDir, { recursive: true });
|
|
109161
109620
|
let loadedPlugins = [];
|
|
@@ -109287,7 +109746,8 @@ async function serve(options = {}) {
|
|
|
109287
109746
|
fileWatcherPool,
|
|
109288
109747
|
terminalPtys,
|
|
109289
109748
|
sessionServerUrl,
|
|
109290
|
-
publishedArtifactStore
|
|
109749
|
+
publishedArtifactStore,
|
|
109750
|
+
previewStateStore
|
|
109291
109751
|
} : null;
|
|
109292
109752
|
const peerManager = peerSetupDeps ? buildPeerManager(peerSetupDeps) : null;
|
|
109293
109753
|
if (peerSetupDeps && !localDirectRef.current) {
|
|
@@ -109400,6 +109860,7 @@ async function serve(options = {}) {
|
|
|
109400
109860
|
log.info("Graceful shutdown initiated");
|
|
109401
109861
|
portDetector.dispose();
|
|
109402
109862
|
publishedArtifactStore.dispose();
|
|
109863
|
+
previewStateStore.dispose();
|
|
109403
109864
|
await previewProxy.stop();
|
|
109404
109865
|
pluginFileWatcher.dispose();
|
|
109405
109866
|
await fileWatcherPool.dispose();
|
|
@@ -109436,4 +109897,4 @@ export {
|
|
|
109436
109897
|
_testing,
|
|
109437
109898
|
serve
|
|
109438
109899
|
};
|
|
109439
|
-
//# sourceMappingURL=serve-
|
|
109900
|
+
//# sourceMappingURL=serve-2J25TKJX.js.map
|