@aefree/pi-plastic 0.5.7 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/plastic-core.ts +45 -5
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project follows semantic versioning for public package releases.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## [0.5.8] - 2026-09-15
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Resolve pending-diff workspace containment and Xlink ownership by filesystem identity when a workspace is accessed through a real filesystem alias, while retaining the caller's lexical path for Plastic commands and displayed paths. Missing deleted or moved ownership ancestors still fail closed.
|
|
15
|
+
- Retry only npm registry E404 availability checks for a bounded period after trusted publishing; a mismatched, malformed, or unauthorized release identity still fails immediately.
|
|
16
|
+
|
|
10
17
|
## [0.5.7] - 2026-09-14
|
|
11
18
|
|
|
12
19
|
### Fixed
|
package/README.md
CHANGED
|
@@ -75,6 +75,7 @@ The Bash guards run only in Pi processes that actually load this package. A proj
|
|
|
75
75
|
- `plastic_branchCreate` supports an explicit parent branch independent of the loaded workspace branch, defaults relative names to the current branch when no parent is supplied, and rejects top-level paths unless `allowRootBranch=true` is explicit.
|
|
76
76
|
- `plastic_diff` remains a disabled alias by design. Use `plastic_status` for changed-path listing; do not diff as routine post-edit validation or checkin preflight. When change-boundary evidence is needed, use `plastic_diffFile` for one exact file, `plastic_workspaceDiff` with selected paths or explicit `allPending=true`, or `plastic_diffRevisions` for an explicit historical pair. Diff responses use small defaults and caller-controlled `maxChars` bounds. Automatic pending-file bases bind each status revision ID to its owning workspace or Xlink repository/server, including mixed-Xlink reviews. If ownership cannot be resolved, that comparison is reported unavailable rather than retrieving a bare revision from the caller repository. Historical and workspace bytes are copied to ASCII-safe package-owned paths before the backend runs, while normalized headers retain the logical Unicode labels.
|
|
77
77
|
- `plastic_status(machineReadable=true, format="json")` returns parsed `items`, aggregate `summary`, and `itemCount` (`total`, `returned`, `omitted`) rather than requiring agents to parse Plastic rows. It returns at most 100 items by default; set `maxItems` (up to 500) when more are intentional. `includeRaw=true` adds the original unbounded Plastic output for diagnostics, so omit it for normal agent use to conserve context tokens.
|
|
78
|
+
- Pending-diff ownership containment uses filesystem identity, so a workspace reached through a symlink, junction, or platform filesystem alias still resolves its workspace and nested Xlink bases safely. Plastic commands and returned labels retain the caller's original lexical path; missing deleted or moved ownership ancestors remain unavailable rather than falling back to a parent repository.
|
|
78
79
|
- Preflight is not routine confirmation. Use it for ambiguous or broad mutation scope, moved/deleted path rewriting, compound operations, or explicit preview requests; otherwise rely on exact targets and the tools' runtime guards.
|
|
79
80
|
- `plastic_patch` generates review patches with `cm patch`, including `clean` and `integration` filters for branch review workflows. It does not expose patch apply. Unqualified `br:/...` selectors are qualified only with the current workspace selector's exact repository; otherwise pass `br:/<branch>@<repository>@<server>`.
|
|
80
81
|
- Bash safety rails block `cm diff` and unsafe interactive `cm merge --merge` usage.
|
package/package.json
CHANGED
package/src/plastic-core.ts
CHANGED
|
@@ -406,6 +406,37 @@ const findCanonicalExistingAncestor = (absolutePath: string): string | null =>
|
|
|
406
406
|
}
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
+
const toFilesystemIdentityPath = async (absolutePath: string): Promise<string> =>
|
|
410
|
+
{
|
|
411
|
+
let candidate = absolutePath;
|
|
412
|
+
const missingSegments: string[] = [];
|
|
413
|
+
while (true)
|
|
414
|
+
{
|
|
415
|
+
try
|
|
416
|
+
{
|
|
417
|
+
const canonicalAncestor = (await fs.realpath(candidate)).replace(/\\/g, "/");
|
|
418
|
+
return missingSegments.reduce((identityPath, segment) => join(identityPath, segment), canonicalAncestor).replace(/\\/g, "/");
|
|
419
|
+
}
|
|
420
|
+
catch (error)
|
|
421
|
+
{
|
|
422
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
423
|
+
if (code !== "ENOENT" && code !== "ENOTDIR")
|
|
424
|
+
{
|
|
425
|
+
throw error;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const parent = dirname(candidate);
|
|
429
|
+
if (parent === candidate)
|
|
430
|
+
{
|
|
431
|
+
throw error;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
missingSegments.unshift(basename(candidate));
|
|
435
|
+
candidate = parent;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
409
440
|
const toggleFirstAsciiLetterCase = (value: string): string | null =>
|
|
410
441
|
{
|
|
411
442
|
const index = value.search(/[A-Za-z]/);
|
|
@@ -1814,9 +1845,10 @@ const createPendingBaseIdentityResolver = (cwd: string, workdir?: string): Pendi
|
|
|
1814
1845
|
});
|
|
1815
1846
|
return workspaceRepository;
|
|
1816
1847
|
};
|
|
1817
|
-
const getXlinkRepository = (candidate: string): Promise<RepositoryIdentity | null> =>
|
|
1848
|
+
const getXlinkRepository = async (candidate: string): Promise<RepositoryIdentity | null> =>
|
|
1818
1849
|
{
|
|
1819
|
-
|
|
1850
|
+
// Use the physical identity for cache keys, but keep candidate lexical for cm.
|
|
1851
|
+
const key = toPathComparisonKeyFromAbsolutePath(await toFilesystemIdentityPath(candidate));
|
|
1820
1852
|
let lookup = xlinkLookups.get(key);
|
|
1821
1853
|
if (!lookup)
|
|
1822
1854
|
{
|
|
@@ -1851,15 +1883,22 @@ const createPendingBaseIdentityResolver = (cwd: string, workdir?: string): Pendi
|
|
|
1851
1883
|
throw new Error(`Plastic status did not provide a base revision ID for '${item.workspacePath}'.`);
|
|
1852
1884
|
}
|
|
1853
1885
|
const root = await getWorkspaceRoot();
|
|
1886
|
+
const rootIdentity = await toFilesystemIdentityPath(root);
|
|
1854
1887
|
const ownershipPath = item.kind === "moved" && item.sourceWorkspacePath
|
|
1855
1888
|
? toNormalizedAbsolutePath(item.sourceWorkspacePath, cwd)
|
|
1856
1889
|
: item.normalizedPath;
|
|
1857
|
-
if (!isWithinPathScope(
|
|
1890
|
+
if (!isWithinPathScope(
|
|
1891
|
+
toPathComparisonKeyFromAbsolutePath(await toFilesystemIdentityPath(ownershipPath)),
|
|
1892
|
+
toPathComparisonKeyFromAbsolutePath(rootIdentity),
|
|
1893
|
+
))
|
|
1858
1894
|
{
|
|
1859
1895
|
throw new Error(`Plastic cannot resolve an owning repository for '${item.workspacePath}' outside the requested workspace.`);
|
|
1860
1896
|
}
|
|
1861
1897
|
let candidate = dirname(ownershipPath);
|
|
1862
|
-
while (isWithinPathScope(
|
|
1898
|
+
while (isWithinPathScope(
|
|
1899
|
+
toPathComparisonKeyFromAbsolutePath(await toFilesystemIdentityPath(candidate)),
|
|
1900
|
+
toPathComparisonKeyFromAbsolutePath(rootIdentity),
|
|
1901
|
+
))
|
|
1863
1902
|
{
|
|
1864
1903
|
// A missing ancestor can be a removed Xlink mount. Status does not
|
|
1865
1904
|
// retain ownership, so parent-repository fallback would be unsafe.
|
|
@@ -1873,7 +1912,8 @@ const createPendingBaseIdentityResolver = (cwd: string, workdir?: string): Pendi
|
|
|
1873
1912
|
item.baseRepository = `${repository.repository}@${repository.server}`;
|
|
1874
1913
|
return `revid:${item.revisionId}@rep:${repository.repository}@repserver:${repository.server}`;
|
|
1875
1914
|
}
|
|
1876
|
-
if (candidate
|
|
1915
|
+
if (toPathComparisonKeyFromAbsolutePath(await toFilesystemIdentityPath(candidate))
|
|
1916
|
+
=== toPathComparisonKeyFromAbsolutePath(rootIdentity))
|
|
1877
1917
|
{
|
|
1878
1918
|
break;
|
|
1879
1919
|
}
|