@skaile/workspaces 0.31.0 → 0.32.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{asset-feeds-6HZDZEIT.js → asset-feeds-2LQIGNUF.js} +7 -7
  3. package/dist/{asset-feeds-6HZDZEIT.js.map → asset-feeds-2LQIGNUF.js.map} +1 -1
  4. package/dist/asset-manager/index.js +5 -5
  5. package/dist/asset-manager/installer.js +4 -4
  6. package/dist/{chunk-VO7QQ76S.js → chunk-2RQ3KLY3.js} +3 -3
  7. package/dist/{chunk-VO7QQ76S.js.map → chunk-2RQ3KLY3.js.map} +1 -1
  8. package/dist/{chunk-G247J4MC.js → chunk-7WHBI3I6.js} +3 -3
  9. package/dist/{chunk-G247J4MC.js.map → chunk-7WHBI3I6.js.map} +1 -1
  10. package/dist/{chunk-MWAEOCU7.js → chunk-AOWKMINN.js} +5 -5
  11. package/dist/{chunk-MWAEOCU7.js.map → chunk-AOWKMINN.js.map} +1 -1
  12. package/dist/{chunk-NQC7ZUXE.js → chunk-BR7RPEHW.js} +3 -3
  13. package/dist/{chunk-NQC7ZUXE.js.map → chunk-BR7RPEHW.js.map} +1 -1
  14. package/dist/{chunk-45RKEHPX.js → chunk-CPYXJJOD.js} +6 -6
  15. package/dist/{chunk-45RKEHPX.js.map → chunk-CPYXJJOD.js.map} +1 -1
  16. package/dist/{chunk-DKAV43NI.js → chunk-DTJNHYFE.js} +2 -2
  17. package/dist/{chunk-DKAV43NI.js.map → chunk-DTJNHYFE.js.map} +1 -1
  18. package/dist/{chunk-QF6LBS2C.js → chunk-GXHJDQ37.js} +78 -24
  19. package/dist/chunk-GXHJDQ37.js.map +1 -0
  20. package/dist/{chunk-4CRKROS5.js → chunk-LO7QEIHT.js} +4 -4
  21. package/dist/{chunk-4CRKROS5.js.map → chunk-LO7QEIHT.js.map} +1 -1
  22. package/dist/{chunk-ZM7UOJR2.js → chunk-MA7HSL5S.js} +2 -2
  23. package/dist/{chunk-ZM7UOJR2.js.map → chunk-MA7HSL5S.js.map} +1 -1
  24. package/dist/{chunk-HH62ECJ7.js → chunk-MG4KFVGV.js} +3 -3
  25. package/dist/{chunk-HH62ECJ7.js.map → chunk-MG4KFVGV.js.map} +1 -1
  26. package/dist/{chunk-WDMH4UMD.js → chunk-N74UQJPN.js} +45 -3
  27. package/dist/chunk-N74UQJPN.js.map +1 -0
  28. package/dist/{chunk-BRBOC53X.js → chunk-NZJDQYNB.js} +2 -2
  29. package/dist/{chunk-BRBOC53X.js.map → chunk-NZJDQYNB.js.map} +1 -1
  30. package/dist/{chunk-LA2YSXEE.js → chunk-PWXN2KHV.js} +3 -3
  31. package/dist/{chunk-LA2YSXEE.js.map → chunk-PWXN2KHV.js.map} +1 -1
  32. package/dist/{chunk-IENYMRZ6.js → chunk-SIW5I47F.js} +8 -8
  33. package/dist/{chunk-IENYMRZ6.js.map → chunk-SIW5I47F.js.map} +1 -1
  34. package/dist/{chunk-CBIPUBUS.js → chunk-SN2RWSB3.js} +3 -3
  35. package/dist/{chunk-CBIPUBUS.js.map → chunk-SN2RWSB3.js.map} +1 -1
  36. package/dist/{chunk-7KOBITAP.js → chunk-YHGJBGFU.js} +3 -3
  37. package/dist/{chunk-7KOBITAP.js.map → chunk-YHGJBGFU.js.map} +1 -1
  38. package/dist/cli/index.js +30 -30
  39. package/dist/connectors/config.js +4 -4
  40. package/dist/connectors/index.js +5 -5
  41. package/dist/connectors/src/connector-types.d.ts +8 -0
  42. package/dist/connectors/src/connector-types.d.ts.map +1 -1
  43. package/dist/core/index.js +3 -3
  44. package/dist/core/runtime-assets.js +2 -2
  45. package/dist/core/src/workspace-config.d.ts +8 -0
  46. package/dist/core/src/workspace-config.d.ts.map +1 -1
  47. package/dist/core/workspace-config.js +1 -1
  48. package/dist/deploy/index.js +3 -3
  49. package/dist/{ensure-sources-JWBUVDW6.js → ensure-sources-CRUZDVPG.js} +7 -7
  50. package/dist/{ensure-sources-JWBUVDW6.js.map → ensure-sources-CRUZDVPG.js.map} +1 -1
  51. package/dist/factory-assets/connectors/deploy.js +5 -5
  52. package/dist/factory-assets/connectors/devserver.js +5 -5
  53. package/dist/factory-assets/connectors/flow/adapter.js +5 -5
  54. package/dist/factory-assets/connectors/flow/engine.js +3 -3
  55. package/dist/factory-assets/connectors/flow/run-flow.js +6 -6
  56. package/dist/factory-assets/connectors/flow.js +5 -5
  57. package/dist/factory-assets/connectors/git/driver.d.ts +54 -0
  58. package/dist/factory-assets/connectors/git/driver.d.ts.map +1 -1
  59. package/dist/factory-assets/connectors/git.js +5 -5
  60. package/dist/factory-assets/connectors/gmail.js +5 -5
  61. package/dist/factory-assets/connectors/googledrive.js +5 -5
  62. package/dist/factory-assets/connectors/local.js +5 -5
  63. package/dist/factory-assets/connectors/mattermost.js +5 -5
  64. package/dist/factory-assets/connectors/memory.js +5 -5
  65. package/dist/factory-assets/connectors/minio.js +5 -5
  66. package/dist/factory-assets/connectors/postgres.js +5 -5
  67. package/dist/factory-assets/connectors/s3.js +5 -5
  68. package/dist/factory-assets/connectors/sharepoint.js +5 -5
  69. package/dist/factory-assets/connectors/sqlite.js +5 -5
  70. package/dist/factory-assets/connectors/static-server.js +5 -5
  71. package/dist/factory-assets/connectors/tunnel.js +5 -5
  72. package/dist/factory-assets/connectors/webdav.js +5 -5
  73. package/dist/factory-assets/connectors/xstate-store.js +5 -5
  74. package/dist/factory-assets/connectors/xstate.js +5 -5
  75. package/dist/{flows-T6RCRKCX.js → flows-W7L2576K.js} +3 -3
  76. package/dist/{flows-T6RCRKCX.js.map → flows-W7L2576K.js.map} +1 -1
  77. package/dist/library/index.js +5 -5
  78. package/dist/library/install.js +4 -4
  79. package/dist/library/src/install/index.d.ts +1 -1
  80. package/dist/library/src/install/index.d.ts.map +1 -1
  81. package/dist/library/src/install/install-from-manifest.d.ts +25 -1
  82. package/dist/library/src/install/install-from-manifest.d.ts.map +1 -1
  83. package/dist/{open-library-ZXTSUPBJ.js → open-library-XZZXXVBP.js} +8 -8
  84. package/dist/{open-library-ZXTSUPBJ.js.map → open-library-XZZXXVBP.js.map} +1 -1
  85. package/dist/{paths-F7RHQUFL.js → paths-RH5CIFX2.js} +6 -6
  86. package/dist/{paths-F7RHQUFL.js.map → paths-RH5CIFX2.js.map} +1 -1
  87. package/dist/{plugin-store-BC3I36HA.js → plugin-store-2UZGNS6M.js} +5 -5
  88. package/dist/{plugin-store-BC3I36HA.js.map → plugin-store-2UZGNS6M.js.map} +1 -1
  89. package/dist/runner/index.js +7 -7
  90. package/dist/sdk/asset-manager.js +5 -5
  91. package/dist/sdk/core.js +3 -3
  92. package/dist/sdk/flow.js +3 -3
  93. package/dist/sdk/index.js +7 -7
  94. package/dist/sdk/runner.js +7 -7
  95. package/dist/{setup-FQ3DE6J6.js → setup-G5UATRCX.js} +5 -5
  96. package/dist/{setup-FQ3DE6J6.js.map → setup-G5UATRCX.js.map} +1 -1
  97. package/dist/{store-client-27YYEWMM.js → store-client-TUC7VSOU.js} +6 -6
  98. package/dist/{store-client-27YYEWMM.js.map → store-client-TUC7VSOU.js.map} +1 -1
  99. package/dist/tui/index.js +7 -7
  100. package/dist/workspace-plugin/adapters/mcp.js +1 -1
  101. package/dist/workspace-plugin/adapters/omp.js +3 -3
  102. package/dist/workspace-plugin/index.js +1 -1
  103. package/package.json +1 -1
  104. package/dist/chunk-QF6LBS2C.js.map +0 -1
  105. package/dist/chunk-WDMH4UMD.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../library/src/install/install-from-manifest.ts"],"names":[],"mappings":"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","file":"chunk-WDMH4UMD.js","sourcesContent":["/**\n * Pointer-only install path.\n *\n * Given an {@link InstallManifest} (the Pointer Triple the Catalog hands\n * clients), this:\n *\n * 1. fetches the asset bytes directly from the upstream repo at the pinned\n * commit (via an injectable fetcher, default {@link fetchAssetFilesFromGitHub});\n * 2. verifies EVERY per-file SHA256 against `manifest.files[].sha256`;\n * 3. verifies the composite SHA256 rollup over the sorted\n * `<path>:<sha256>\\n` lines against `manifest.sha256`;\n * 4. writes the verified bytes under `<libraryRoot>/@<publisher>/<name>/<version>`.\n *\n * Any hash mismatch is a hard failure — bytes are not written if per-file\n * verification fails, and the composite check is the second gate.\n *\n * @docLink packages/library/concepts#install\n */\n\nimport { createHash } from \"node:crypto\";\nimport { mkdir, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport { parseAssetRef } from \"@skaile/workspaces/core\";\nimport type { InstallManifest } from \"@skaile/workspaces/types\";\n\n/**\n * Validate a single manifest-supplied file path against path traversal.\n *\n * `manifest.files[].path` comes off the network — the attacker controls the\n * entire manifest (and therefore every hash in it), so hash verification can\n * NEVER mitigate a malicious *destination*. A path like\n * `../../../../home/user/.bashrc` or an absolute path would escape\n * `installPath` and yield an arbitrary file write. Every path is checked\n * BEFORE any fetch or write happens; the first bad path aborts the entire\n * install with a hard throw.\n *\n * A path is rejected if ANY of:\n * - it is empty;\n * - it is absolute (`path.isAbsolute`) or has a leading slash;\n * - it contains a `..` segment;\n * - `path.resolve(installPath, p)` is not `installPath` itself and does not\n * start with `installPath + path.sep` (escapes the install dir).\n *\n * @throws An error matching `/unsafe path in manifest/` on the first bad path.\n */\nfunction assertSafeManifestPath(installPath: string, p: string): void {\n const bad = (): never => {\n throw new Error(`unsafe path in manifest: ${p}`);\n };\n if (!p || p.length === 0) bad();\n if (isAbsolute(p) || p.startsWith(\"/\") || p.startsWith(\"\\\\\")) bad();\n const segments = p.split(/[/\\\\]/);\n if (segments.some((s) => s === \"..\")) bad();\n const resolved = resolve(installPath, p);\n if (resolved !== installPath && !resolved.startsWith(installPath + sep)) bad();\n}\n\n/**\n * Validate one component (publisher / name / version) of the attacker-controlled\n * `manifest.ref`.\n *\n * `installPath` is built by joining these components onto `libraryRoot`. A\n * component containing a path separator or a `..` / leading-dot segment would\n * let `installPath` itself escape `libraryRoot` — yielding an arbitrary file\n * write AND, via the pre-write `rm(installPath, { recursive })`, an arbitrary\n * recursive delete. The per-file `assertSafeManifestPath` guard cannot catch\n * this: it only validates each `f.path` *relative to* `installPath`, never\n * `installPath` itself. Components must be plain identifier-like strings.\n *\n * @throws An error matching `/unsafe ref/` on a bad component.\n */\nfunction assertSafeRefComponent(kind: string, value: string): void {\n // Reject empty, leading-dot (covers `.` and `..`), path separators, and\n // ASCII control characters (NUL et al.) — fails a malformed segment fast\n // with a domain-specific error rather than a raw EINVAL deeper in.\n const hasControlChar = [...value].some((c) => c.charCodeAt(0) < 0x20);\n if (!value || value.startsWith(\".\") || /[/\\\\]/.test(value) || hasControlChar) {\n throw new Error(`unsafe ref ${kind} component in manifest: '${value}'`);\n }\n}\n\n/**\n * Fetcher signature — fetches `{ path → Buffer }` for the requested paths at\n * a pinned upstream commit. {@link fetchAssetFilesFromGitHub} is the default\n * production implementation; tests inject a mock.\n */\nexport type AssetFetcher = (opts: {\n url: string;\n commitSha: string;\n paths: string[];\n}) => Promise<Record<string, Buffer>>;\n\n/**\n * Options for {@link installFromManifest}.\n */\nexport interface InstallOpts {\n /** The pointer-only install manifest from the Catalog. */\n manifest: InstallManifest;\n /** Filesystem root the asset is installed under. */\n libraryRoot: string;\n /**\n * Pluggable fetcher so tests can inject mock data. Defaults to\n * {@link fetchAssetFilesFromGitHub} in production.\n */\n fetcher?: AssetFetcher;\n}\n\n/**\n * Result of a successful {@link installFromManifest} call.\n */\nexport interface InstallResult {\n /** Absolute path the asset files were written to. */\n installPath: string;\n}\n\n/**\n * Fetch, verify, and write an asset's files per its {@link InstallManifest}.\n *\n * @throws An error matching `/hash mismatch/i` if any per-file SHA256 differs.\n * @throws An error matching `/composite hash mismatch/i` if the rollup differs.\n * @throws An error if the ref is malformed or the fetcher omits a file.\n */\nexport async function installFromManifest(opts: InstallOpts): Promise<InstallResult> {\n const { manifest, libraryRoot } = opts;\n const fetcher =\n opts.fetcher ?? (await import(\"./source-fetch-github.js\")).fetchAssetFilesFromGitHub;\n\n // 1. Parse ref into install path: `skill:@x/git#1.0.0` → `<root>/@x/git/1.0.0`.\n // Done first so the C1 path-traversal guard can resolve against installPath\n // BEFORE any network fetch happens. The ref is attacker-controlled, so each\n // segment is further validated as a safe path component (H1) after parsing.\n // `parseAssetRef` is the single canonical grammar (`kind:@publisher/name#version`);\n // the legacy trailing-`@version` shape was removed in core and is rejected here.\n let parsed: ReturnType<typeof parseAssetRef>;\n try {\n parsed = parseAssetRef(manifest.ref);\n } catch (e) {\n throw new Error(`invalid ref: ${manifest.ref}: ${e instanceof Error ? e.message : String(e)}`);\n }\n const publisher = parsed.publisher;\n const name = parsed.name;\n const version = parsed.pin;\n if (!publisher) throw new Error(`invalid ref: ${manifest.ref} (missing publisher)`);\n if (!version) throw new Error(`invalid ref: ${manifest.ref} (missing version pin)`);\n assertSafeRefComponent(\"publisher\", publisher);\n assertSafeRefComponent(\"name\", name);\n assertSafeRefComponent(\"version\", version);\n const root = resolve(libraryRoot);\n const installPath = resolve(join(root, `@${publisher}`, name, version));\n // Defense in depth: even with the segment guards above, assert the built\n // installPath stays strictly inside libraryRoot before any write or `rm`.\n if (installPath !== root && !installPath.startsWith(root + sep)) {\n throw new Error(`unsafe install path: ref '${manifest.ref}' resolves outside the library root`);\n }\n\n // 2. [C1] Validate EVERY manifest path against traversal BEFORE fetching.\n // The attacker controls the manifest (and thus all hashes) — hashes verify\n // content, never destination. Reject the whole install on the first bad path.\n for (const f of manifest.files) {\n assertSafeManifestPath(installPath, f.path);\n }\n\n // 3. Fetch all files at the pinned commit.\n const paths = manifest.files.map((f) => f.path);\n const bytes = await fetcher({\n url: manifest.source.url,\n commitSha: manifest.source.commitSha,\n paths,\n });\n\n // 4. Verify per-file SHA256 — hard fail before writing anything. The path\n // key used to read from the fetcher result is re-guarded as defense in depth.\n for (const f of manifest.files) {\n assertSafeManifestPath(installPath, f.path);\n const buf = bytes[f.path];\n if (!buf) throw new Error(`fetcher returned no bytes for ${f.path}`);\n const actual = createHash(\"sha256\").update(buf).digest(\"hex\");\n if (actual !== f.sha256) {\n throw new Error(`hash mismatch for ${f.path}: expected ${f.sha256}, got ${actual}`);\n }\n }\n\n // 5. Verify composite SHA256 over the sorted `<path>:<sha256>\\n` lines.\n const sortedLines = manifest.files\n .slice()\n .sort((a, b) => a.path.localeCompare(b.path))\n .map((e) => `${e.path}:${e.sha256}\\n`)\n .join(\"\");\n const actualComposite = createHash(\"sha256\").update(sortedLines, \"utf-8\").digest(\"hex\");\n if (actualComposite !== manifest.sha256) {\n throw new Error(`composite hash mismatch: expected ${manifest.sha256}, got ${actualComposite}`);\n }\n\n // 6. [M1] Write all verified files. A failed install must leave NO partial\n // directory: any prior `installPath` is removed first (clean overwrite), and\n // a mid-loop write failure removes the partially-written tree before\n // rethrowing.\n await rm(installPath, { recursive: true, force: true });\n try {\n for (const f of manifest.files) {\n const target = join(installPath, f.path);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, bytes[f.path]!);\n }\n } catch (e) {\n await rm(installPath, { recursive: true, force: true }).catch(() => {});\n throw e;\n }\n\n return { installPath };\n}\n"]}