@wenathlan/devthink 2.0.12 → 2.0.14
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 +20 -0
- package/accounts.ts +2087 -0
- package/agent.ts +2331 -0
- package/antigravity.ts +2139 -0
- package/apifreeze.ts +4146 -0
- package/auth.ts +642 -0
- package/background.ts +16837 -0
- package/bridge.ts +2080 -0
- package/build.ts +54 -0
- package/bun.ts +26 -0
- package/capture.ts +1614 -0
- package/checksecrets.ts +72 -0
- package/cjs.ts +9 -0
- package/cli.ts +3500 -0
- package/commands.ts +1101 -0
- package/companion.ts +450 -0
- package/compatibility.ts +37 -0
- package/config.ts +3137 -0
- package/constants.ts +943 -0
- package/core.ts +1473 -0
- package/crossbrowser.ts +1359 -0
- package/dashdone.ts +605 -0
- package/data.ts +943 -0
- package/database.ts +133 -0
- package/debug.ts +850 -0
- package/deno.ts +27 -0
- package/devthink.ts +707 -0
- package/dist/accounts.d.ts +409 -0
- package/dist/agent.d.ts +837 -0
- package/dist/antigravity.d.ts +380 -0
- package/dist/apifreeze.d.ts +113 -0
- package/dist/auth.d.ts +224 -0
- package/{bridge.d.ts → dist/bridge.d.ts} +1 -1
- package/dist/capture.d.ts +595 -0
- package/dist/cjs.d.ts +6 -0
- package/{cli.js → dist/cli.js} +2 -2
- package/dist/commands.d.ts +310 -0
- package/dist/compatibility.d.ts +17 -0
- package/dist/config.d.ts +1853 -0
- package/dist/constants.d.ts +637 -0
- package/dist/core.d.ts +361 -0
- package/dist/data.d.ts +330 -0
- package/dist/database.d.ts +24 -0
- package/dist/debug.d.ts +270 -0
- package/dist/engine.d.ts +74 -0
- package/dist/environments.d.ts +244 -0
- package/dist/evidence.d.ts +277 -0
- package/dist/export.d.ts +311 -0
- package/dist/fingerprint.d.ts +594 -0
- package/dist/flow.d.ts +300 -0
- package/dist/gates.d.ts +103 -0
- package/dist/identity.d.ts +51 -0
- package/dist/ids.d.ts +7 -0
- package/dist/llm.d.ts +652 -0
- package/dist/models.d.ts +1807 -0
- package/dist/net.d.ts +325 -0
- package/dist/neutral.d.ts +41 -0
- package/dist/oauth.d.ts +703 -0
- package/dist/page.d.ts +1166 -0
- package/dist/perf.d.ts +481 -0
- package/dist/plan.d.ts +134 -0
- package/dist/project.d.ts +395 -0
- package/dist/providers.d.ts +37 -0
- package/dist/quota.d.ts +453 -0
- package/dist/recovery.d.ts +621 -0
- package/dist/request.d.ts +1492 -0
- package/dist/run.d.ts +779 -0
- package/dist/runtime.d.ts +212 -0
- package/dist/search.d.ts +375 -0
- package/dist/security.d.ts +534 -0
- package/dist/serve.d.ts +136 -0
- package/dist/session.d.ts +296 -0
- package/dist/storage.d.ts +53 -0
- package/dist/streaming.d.ts +1119 -0
- package/dist/swarm.d.ts +328 -0
- package/dist/tools.d.ts +170 -0
- package/dist/types.d.ts +8357 -0
- package/dist/utils.d.ts +82 -0
- package/dist/version.d.ts +7 -0
- package/dist/versionregistry.d.ts +406 -0
- package/dist/views.d.ts +579 -0
- package/dist/workbenchsession.d.ts +69 -0
- package/dist/workflow.d.ts +649 -0
- package/docs/readme.md +92 -0
- package/docs/schemas/antigravity.json +192 -0
- package/engine.ts +1524 -0
- package/environments.ts +1005 -0
- package/evidence.ts +643 -0
- package/export.ts +889 -0
- package/fingerprint.ts +1458 -0
- package/flow.ts +1190 -0
- package/gates.ts +307 -0
- package/gateway.ts +34 -0
- package/hardening.ts +191 -0
- package/headless.ts +369 -0
- package/http.ts +2110 -0
- package/identity.ts +246 -0
- package/ids.ts +26 -0
- package/index.ts +369 -0
- package/llm.ts +2682 -0
- package/mcp.ts +1835 -0
- package/{memory.js → memory.ts} +3834 -2310
- package/models.ts +3805 -0
- package/modes.ts +43 -0
- package/net.ts +1214 -0
- package/neutral.ts +362 -0
- package/node.ts +26 -0
- package/oauth.ts +2216 -0
- package/offscreen.ts +170 -0
- package/pack.ts +956 -0
- package/package.json +175 -82
- package/page.ts +7824 -0
- package/pagebridge.ts +1116 -0
- package/perf.ts +957 -0
- package/plan.ts +672 -0
- package/plugin.ts +76 -0
- package/{policy.js → policy.ts} +5073 -4679
- package/prisma.config.ts +27 -0
- package/progress.ts +1308 -0
- package/project.ts +1291 -0
- package/protocol.ts +4187 -0
- package/providers.ts +246 -0
- package/quota.ts +1887 -0
- package/recovery.ts +2433 -0
- package/request.ts +4482 -0
- package/run.ts +2135 -0
- package/runtime.ts +526 -0
- package/search.ts +1760 -0
- package/security.ts +1673 -0
- package/serve.ts +419 -0
- package/server.ts +5538 -0
- package/session.ts +1162 -0
- package/storage.ts +182 -0
- package/streaming.ts +4101 -0
- package/swarm.ts +884 -0
- package/sync.ts +56 -0
- package/tests/agentcert.test.ts +128 -0
- package/tests/agentfleet.test.ts +214 -0
- package/tests/agentreview.test.ts +241 -0
- package/tests/agentstream.test.ts +335 -0
- package/tests/agentwork.test.ts +356 -0
- package/tests/apifreeze.test.ts +290 -0
- package/tests/apimap.test.ts +112 -0
- package/tests/approvalgate.test.ts +175 -0
- package/tests/artifactmanifest.test.ts +204 -0
- package/tests/attentionfeed.test.ts +163 -0
- package/tests/backgroundruns.test.ts +100 -0
- package/tests/batchscheduling.test.ts +131 -0
- package/tests/blackboard.test.ts +238 -0
- package/tests/bridge.test.ts +1088 -0
- package/tests/browsercoverage.test.ts +134 -0
- package/tests/browserpolyfills.test.ts +124 -0
- package/tests/build.mjs +1260 -0
- package/tests/clientauth.test.ts +223 -0
- package/tests/clitools.test.ts +578 -0
- package/tests/confirmgates.test.ts +190 -0
- package/tests/containerpack.test.ts +143 -0
- package/tests/controlflow.test.ts +2120 -0
- package/tests/coordination.test.ts +345 -0
- package/tests/copyscreen.test.ts +139 -0
- package/tests/core.test.ts +417 -0
- package/tests/costcert.test.ts +95 -0
- package/tests/crossbrowser.test.ts +115 -0
- package/tests/cspaudit.test.ts +79 -0
- package/tests/dashdone.test.ts +615 -0
- package/tests/datagrid.test.ts +137 -0
- package/tests/doccheck.test.ts +107 -0
- package/tests/dombatch.test.ts +109 -0
- package/tests/emulation.test.ts +282 -0
- package/tests/environments.test.ts +259 -0
- package/tests/evidenceviews.test.ts +88 -0
- package/tests/exporttools.test.ts +185 -0
- package/tests/firefoxprep.test.ts +134 -0
- package/tests/flowlibrary.test.ts +242 -0
- package/tests/flowrun.test.ts +303 -0
- package/tests/forensics.test.ts +439 -0
- package/tests/gateway.test.ts +939 -0
- package/tests/headlesslib.test.ts +171 -0
- package/tests/httpstream.test.ts +277 -0
- package/tests/immutablelog.test.ts +157 -0
- package/tests/inboundguard.test.ts +177 -0
- package/tests/lazyload.test.ts +72 -0
- package/tests/librarymodes.test.ts +623 -0
- package/tests/llm.test.ts +696 -0
- package/tests/manifest.test.ts +226 -0
- package/tests/maskinputs.test.ts +150 -0
- package/tests/matrixverify.test.ts +111 -0
- package/tests/mavenpack.test.ts +75 -0
- package/tests/mcpserver.test.ts +704 -0
- package/tests/memory.test.ts +4155 -0
- package/tests/memorycare.test.ts +230 -0
- package/tests/migrateplan.test.ts +349 -0
- package/tests/minimization.test.ts +414 -0
- package/tests/modelroute.test.ts +112 -0
- package/tests/multiagent.test.ts +440 -0
- package/tests/nativebridge.test.ts +807 -0
- package/tests/navigation.test.ts +284 -0
- package/tests/nugetpack.test.ts +83 -0
- package/tests/orchestration.test.ts +449 -0
- package/tests/originpolicy.test.ts +405 -0
- package/tests/outputcompare.test.ts +97 -0
- package/tests/package.test.ts +91 -0
- package/tests/pagebridge.test.ts +5909 -0
- package/tests/pentest.test.ts +75 -0
- package/tests/perfrecords.test.ts +257 -0
- package/tests/permdiff.test.ts +85 -0
- package/tests/phishguard.test.ts +128 -0
- package/tests/pickerviews.test.ts +155 -0
- package/tests/pipelines.test.ts +381 -0
- package/tests/planlint.test.ts +240 -0
- package/tests/planreview.test.ts +383 -0
- package/tests/platformtargets.test.ts +56 -0
- package/tests/policy.test.ts +8284 -0
- package/tests/portability.test.ts +96 -0
- package/tests/progress.test.ts +1036 -0
- package/tests/promptlibrary.test.ts +116 -0
- package/tests/protocol.test.ts +4793 -0
- package/tests/publishingpipeline.test.ts +314 -0
- package/tests/quickactions.test.ts +164 -0
- package/tests/readiness.test.ts +78 -0
- package/tests/recipes.test.ts +163 -0
- package/tests/redactshots.test.ts +216 -0
- package/tests/registernvidiakeys.ts +51 -0
- package/tests/relayserve.test.ts +304 -0
- package/tests/releasenotes.test.ts +276 -0
- package/tests/resilience.test.ts +467 -0
- package/tests/resourceaware.test.ts +187 -0
- package/tests/runbudget.test.ts +125 -0
- package/tests/runhistory.test.ts +299 -0
- package/tests/runreplay.test.ts +113 -0
- package/tests/runresume.test.ts +184 -0
- package/tests/runstate.test.ts +288 -0
- package/tests/runtimeadapters.test.ts +251 -0
- package/tests/safariskeleton.test.ts +115 -0
- package/tests/sandboxframe.test.ts +196 -0
- package/tests/sbom.test.ts +105 -0
- package/tests/secretvault.test.ts +188 -0
- package/tests/selcache.test.ts +111 -0
- package/tests/sessioninterface.test.ts +791 -0
- package/tests/sessions.test.ts +591 -0
- package/tests/siteprefs.test.ts +228 -0
- package/tests/snapshotdelta.test.ts +165 -0
- package/tests/statusviews.test.ts +237 -0
- package/tests/stepmeter.test.ts +200 -0
- package/tests/streamparse.test.ts +163 -0
- package/tests/stylepruning.test.ts +90 -0
- package/tests/surfaces.test.ts +282 -0
- package/tests/sweep.test.ts +78 -0
- package/tests/syncbridge.test.ts +164 -0
- package/tests/taskqueue.test.ts +206 -0
- package/tests/toolcalls.test.ts +336 -0
- package/tests/toolcatalog.test.ts +201 -0
- package/tests/torture-agent.test.ts +706 -0
- package/tests/torture-auth.test.ts +461 -0
- package/tests/torture-commands.test.ts +895 -0
- package/tests/torture-data.test.ts +923 -0
- package/tests/torture-environments.test.ts +285 -0
- package/tests/torture-export.test.ts +263 -0
- package/tests/torture-http.test.ts +1379 -0
- package/tests/torture-llm.test.ts +808 -0
- package/tests/torture-memory.test.ts +1084 -0
- package/tests/torture-plan.test.ts +829 -0
- package/tests/torture-run.test.ts +626 -0
- package/tests/torture-session.test.ts +928 -0
- package/tests/torture-swarm.test.ts +1245 -0
- package/tests/torture-tools.test.ts +964 -0
- package/tests/torture-workflow.test.ts +2354 -0
- package/tests/tourviews.test.ts +52 -0
- package/tests/transparency.test.ts +147 -0
- package/tests/trigger.test.ts +941 -0
- package/tests/v1sunset.test.ts +428 -0
- package/tests/virtlist.test.ts +94 -0
- package/tests/vision.test.ts +540 -0
- package/tests/vsixpack.test.ts +145 -0
- package/tests/webapi.test.ts +523 -0
- package/tests/workflow.test.ts +1785 -0
- package/tests/workfloweditor.test.ts +759 -0
- package/tests/xpipack.test.ts +128 -0
- package/tools.ts +734 -0
- package/types.ts +9908 -0
- package/umd.ts +9 -0
- package/utils.ts +249 -0
- package/version.ts +8 -0
- package/versionregistry.ts +1310 -0
- package/views.ts +2153 -0
- package/vitest.config.ts +12 -0
- package/web/App.tsx +78 -0
- package/web/README.md +47 -0
- package/web/capacitor.config.ts +46 -0
- package/web/const.ts +19 -0
- package/web/control.shell.tsx +66 -0
- package/web/dashboardpage.ts +554 -0
- package/web/db.ts +493 -0
- package/web/design.html +541 -0
- package/web/gatewayview/Gateway.tsx +345 -0
- package/web/gatewayview/config.ts +1042 -0
- package/web/gatewayview/definition.ts +174 -0
- package/web/icons.ts +15 -0
- package/{manifest.json → web/manifest.json} +1 -1
- package/web/optionspage.ts +691 -0
- package/web/popup.ts +1651 -0
- package/web/schema.prisma +176 -0
- package/web/sidepanel.ts +7941 -0
- package/web/sitemanifest.json +13 -0
- package/web/terminal-ui.tsx +622 -0
- package/web/transparencypage.ts +541 -0
- package/web/vite.config.ts +39 -0
- package/web/workspace.ts +23 -0
- package/workbenchmemory.ts +104 -0
- package/workbenchsession.ts +304 -0
- package/workflow.ts +4495 -0
- package/bridge.d.ts.map +0 -1
- package/bridge.js +0 -1877
- package/bun.d.ts.map +0 -1
- package/bun.js +0 -52329
- package/caps/background.json +0 -712
- package/caps/cli.json +0 -360
- package/caps/library.json +0 -3611
- package/caps/mcp.json +0 -126
- package/caps/pagebridge.json +0 -377
- package/caps/popup.json +0 -51
- package/caps/sidepanel.json +0 -522
- package/checksums.txt +0 -142
- package/cli.d.ts.map +0 -1
- package/companion.d.ts.map +0 -1
- package/companion.js +0 -389
- package/crossbrowser.d.ts.map +0 -1
- package/crossbrowser.js +0 -1191
- package/dashdone.d.ts.map +0 -1
- package/dashdone.js +0 -368
- package/deno.d.ts.map +0 -1
- package/deno.js +0 -52325
- package/devthink.umd.js +0 -52346
- package/fixtures/bun.ts +0 -7
- package/fixtures/cjs.js +0 -6
- package/fixtures/deno.ts +0 -6
- package/fixtures/example-org-pagestate.json +0 -22
- package/fixtures/importers/automa-workflow.json +0 -18
- package/fixtures/importers/selenium-side.json +0 -20
- package/fixtures/importers/tabular-plan.csv +0 -7
- package/fixtures/importers/uivision-macro.json +0 -10
- package/fixtures/importers/v1-plan.json +0 -13
- package/fixtures/mcp-client.mjs +0 -14
- package/fixtures/pages/checkout.html +0 -192
- package/fixtures/pages/dashboard.html +0 -110
- package/fixtures/pages/feed.html +0 -113
- package/fixtures/pages/product-grid.html +0 -150
- package/fixtures/plans/form-inventory-plan.json +0 -11
- package/fixtures/plans/release-notes-plan.json +0 -11
- package/fixtures/recipes/agentscompete.json +0 -11
- package/fixtures/recipes/agentsforms.json +0 -11
- package/fixtures/recipes/agentsmonitor.json +0 -11
- package/fixtures/recipes/agentsreview.json +0 -29
- package/fixtures/recipes/agentswarm.json +0 -11
- package/fixtures/recipes/fillcalendar.json +0 -12
- package/fixtures/recipes/fillcheckout.json +0 -13
- package/fixtures/recipes/filldependentdropdowns.json +0 -12
- package/fixtures/recipes/fillfromcsv.json +0 -13
- package/fixtures/recipes/fillloginform.json +0 -12
- package/fixtures/recipes/fillmultistepwizard.json +0 -13
- package/fixtures/recipes/fillregistration.json +0 -12
- package/fixtures/recipes/fillsearchfilters.json +0 -12
- package/fixtures/recipes/filltypeahead.json +0 -11
- package/fixtures/recipes/monitoralerts.json +0 -12
- package/fixtures/recipes/monitoravailability.json +0 -11
- package/fixtures/recipes/monitornetworkquiet.json +0 -12
- package/fixtures/recipes/monitorpagechanges.json +0 -11
- package/fixtures/recipes/monitorpricechange.json +0 -11
- package/fixtures/recipes/monitorschedule.json +0 -12
- package/fixtures/recipes/scrapedynamicprices.json +0 -13
- package/fixtures/recipes/scrapeimagegallery.json +0 -12
- package/fixtures/recipes/scrapeinfinitefeed.json +0 -13
- package/fixtures/recipes/scrapenestedlists.json +0 -12
- package/fixtures/recipes/scrapeproductgrid.json +0 -12
- package/fixtures/recipes/scrapereviews.json +0 -12
- package/fixtures/recipes/scrapesearchresults.json +0 -12
- package/fixtures/recipes/scrapetablepagination.json +0 -12
- package/fixtures/recipes/testaccessibility.json +0 -34
- package/fixtures/recipes/testconsoleerrors.json +0 -12
- package/fixtures/recipes/testdeeplinks.json +0 -12
- package/fixtures/recipes/testformvalidation.json +0 -13
- package/fixtures/recipes/testlayoutshift.json +0 -12
- package/fixtures/recipes/testlinkcheck.json +0 -12
- package/fixtures/recipes/testloadtiming.json +0 -12
- package/fixtures/recipes/testvisualdiff.json +0 -12
- package/fixtures/workflows/changelog-digest-workflow.json +0 -13
- package/fixtures/workflows/form-survey-workflow.json +0 -13
- package/gallery.json +0 -706
- package/hardening.d.ts.map +0 -1
- package/hardening.js +0 -188
- package/headless.d.ts.map +0 -1
- package/headless.js +0 -11522
- package/http.d.ts.map +0 -1
- package/http.js +0 -2165
- package/index.cjs +0 -54682
- package/index.d.ts.map +0 -1
- package/index.js +0 -52310
- package/index.neutral.js +0 -46343
- package/mcp.d.ts.map +0 -1
- package/mcp.js +0 -20515
- package/memory.d.ts.map +0 -1
- package/nativehost.template.json +0 -10
- package/node.cjs +0 -54686
- package/node.d.ts.map +0 -1
- package/pack.d.ts.map +0 -1
- package/pack.js +0 -805
- package/policy.d.ts.map +0 -1
- package/progress.d.ts.map +0 -1
- package/progress.js +0 -645
- package/protocol.d.ts.map +0 -1
- package/protocol.js +0 -8334
- package/schemas/audit.schema.json +0 -226
- package/schemas/capability.schema.json +0 -113
- package/schemas/envelope.schema.json +0 -162
- package/schemas/memory.schema.json +0 -51
- package/schemas/observation.schema.json +0 -113
- package/schemas/plan.schema.json +0 -208
- package/schemas/progress.schema.json +0 -126
- package/schemas/proposal.schema.json +0 -68
- package/schemas/session.schema.json +0 -82
- package/schemas/tool.schema.json +0 -94
- package/server.d.ts.map +0 -1
- package/server.js +0 -28877
- package/umd-example.html +0 -18
- package/umd.d.ts.map +0 -1
- /package/{bun.d.ts → dist/bun.d.ts} +0 -0
- /package/{cli.d.ts → dist/cli.d.ts} +0 -0
- /package/{companion.d.ts → dist/companion.d.ts} +0 -0
- /package/{crossbrowser.d.ts → dist/crossbrowser.d.ts} +0 -0
- /package/{dashdone.d.ts → dist/dashdone.d.ts} +0 -0
- /package/{deno.d.ts → dist/deno.d.ts} +0 -0
- /package/{hardening.d.ts → dist/hardening.d.ts} +0 -0
- /package/{headless.d.ts → dist/headless.d.ts} +0 -0
- /package/{http.d.ts → dist/http.d.ts} +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{mcp.d.ts → dist/mcp.d.ts} +0 -0
- /package/{memory.d.ts → dist/memory.d.ts} +0 -0
- /package/{node.d.ts → dist/node.d.ts} +0 -0
- /package/{pack.d.ts → dist/pack.d.ts} +0 -0
- /package/{policy.d.ts → dist/policy.d.ts} +0 -0
- /package/{progress.d.ts → dist/progress.d.ts} +0 -0
- /package/{protocol.d.ts → dist/protocol.d.ts} +0 -0
- /package/{server.d.ts → dist/server.d.ts} +0 -0
- /package/{umd.d.ts → dist/umd.d.ts} +0 -0
package/pack.ts
ADDED
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
/** The pack module of the 1.1.90 consolidation: every correlated variation of the publishing pipeline contract and all its packagers interned in this one file, so the module family carries one surface without duplicate variations. */
|
|
2
|
+
|
|
3
|
+
/* ── Merged from artifactmanifest.ts: the 1.1.90 consolidation interns the correlated artifactmanifest logic here, so no variation of the same file lives beside another. ── */
|
|
4
|
+
import type {
|
|
5
|
+
artifactmanifestdocument,
|
|
6
|
+
artifactmanifestentry,
|
|
7
|
+
containerbuildstage,
|
|
8
|
+
containerexposedsurface,
|
|
9
|
+
mavenattachedartifact,
|
|
10
|
+
mavenpommetadata,
|
|
11
|
+
nugetcontententry,
|
|
12
|
+
nugetpackmetadata,
|
|
13
|
+
sbominventoryinput,
|
|
14
|
+
vsixmanifestfields,
|
|
15
|
+
vsixpackinput,
|
|
16
|
+
vsixpackoutput,
|
|
17
|
+
} from "./types.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Artifactmanifest of the 1.1.87 publishing pipeline family.
|
|
21
|
+
* Every release artifact manifest concern of the publishing pipeline lives in this one pure module: the manifest document the build and the release workflow emit (every artifact with its name, its byte size, its sha256 checksum and its publishing channels), the channel resolution that maps one artifact name onto the channels it ships through, the check that asserts a built set matches the manifest exactly, and the rollback pin that freezes the previous release artifact set through its immutable tag. The module stays pure: the manifest carries no timestamp so the same artifact set renders byte identical on every run, the artifact records reach the module through injected seams only, and the rollback pin references only immutable tags of this repository — no download url, no vendor endpoint and no mutable reference ever enters the manifest.
|
|
22
|
+
* Example: `const manifest = artifactmanifestof({ version: "1.1.87", artifacts: [{ name: "devthink1.1.87.zip", size: 1024, checksum: "abcd" }] });`
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** The publishing channels the release operates: the github release asset channel every artifact rides, the npm channel (npmjs and GitHub Packages), the nuget channel, the maven channel, the container channel, the vscode channel, the firefox channel, the safari channel, the chromium channel, the site channel, the declarations channel and the provenance channel — one artifact may ride several channels (the extension zip rides the chromium, nuget and maven channels at once). */
|
|
26
|
+
export function artifactchannels(): string[] {
|
|
27
|
+
return [
|
|
28
|
+
"github",
|
|
29
|
+
"npmjs",
|
|
30
|
+
"githubpackages",
|
|
31
|
+
"nuget",
|
|
32
|
+
"maven",
|
|
33
|
+
"container",
|
|
34
|
+
"vscode",
|
|
35
|
+
"firefox",
|
|
36
|
+
"safari",
|
|
37
|
+
"chromium",
|
|
38
|
+
"site",
|
|
39
|
+
"declarations",
|
|
40
|
+
"provenance",
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Resolves the publishing channels one artifact name belongs to: the version stamped artifact names map onto the channels the release workflow operates, and an unrecognized name still rides the github asset channel because every release artifact attaches to the release. The mappings answer under the devthink release asset names the merged release lane assembles (the names the release notes and their tests record) beside the extension lineage names the channel history still accepts (the historical artifact names of the 1.1.x line the artifact manifest tests exercise as accepted aliases). */
|
|
45
|
+
export function artifactchannelof(name: string, version: string): string[] {
|
|
46
|
+
const channels: string[] = ["github"];
|
|
47
|
+
if (name.startsWith("dist/")) return ["npmjs", "githubpackages"];
|
|
48
|
+
if (name === `wenathlan-devthink-${version}.tgz`) return ["github", "npmjs", "githubpackages"];
|
|
49
|
+
if (name === `devthink.${version}.nupkg`) return ["github", "nuget"];
|
|
50
|
+
if (name === `devthink-${version}.pom`) return ["github", "maven"];
|
|
51
|
+
if (name === `devthink-${version}.gem`) return ["github", "rubygems"];
|
|
52
|
+
if (name.startsWith("devthink-container.")) return ["github", "container"];
|
|
53
|
+
if (name === `devthink-${version}-source.zip`) return ["github", "chromium"];
|
|
54
|
+
if (name === `wenathlan-extension-${version}.tgz`) return ["github", "npmjs", "githubpackages"];
|
|
55
|
+
if (name === `extension.${version}.nupkg`) return ["github", "nuget"];
|
|
56
|
+
if (name === `extension-${version}.pom`) return ["github", "maven"];
|
|
57
|
+
if (name === `extension-${version}.gem`) return ["github", "rubygems"];
|
|
58
|
+
if (name.startsWith("extension-container.")) return ["github", "container"];
|
|
59
|
+
if (name === `devthink-vscode-${version}.vsix`) return ["github", "vscode"];
|
|
60
|
+
if (name === `devthink-firefox-${version}.xpi`) return ["github", "firefox"];
|
|
61
|
+
if (name === `devthink-safari-${version}.zip`) return ["github", "safari"];
|
|
62
|
+
if (name === `devthink${version}.zip`) return ["github", "chromium", "nuget", "maven"];
|
|
63
|
+
if (name === `devthink-site-${version}.zip`) return ["github", "site"];
|
|
64
|
+
if (name === `devthink-declarations-${version}.zip`) return ["github", "declarations", "maven"];
|
|
65
|
+
if (name === `devthink-nativehost-${version}.template.json`) return ["github", "chromium"];
|
|
66
|
+
if (name === `devthink-sbom-${version}.json`) return ["github", "provenance"];
|
|
67
|
+
if (name === `devthink-attestations-${version}.json`) return ["github", "provenance"];
|
|
68
|
+
if (name === `devthink-artifactmanifest-${version}.json`) return ["github", "provenance"];
|
|
69
|
+
if (name === `extension-${version}-source.zip`) return ["github", "chromium"];
|
|
70
|
+
if (name === "RELEASENOTES.md" || name === "SHA256SUMS.txt") return ["github"];
|
|
71
|
+
return channels;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Builds the artifact manifest of one release: every artifact with its name, its size, its checksum and its channels — the document carries no timestamp so the build and the release workflow render the same artifact set byte identically. */
|
|
75
|
+
export function artifactmanifestof(input: {
|
|
76
|
+
version: string;
|
|
77
|
+
artifacts: Array<{ name: string; size: number; checksum: string }>;
|
|
78
|
+
}): artifactmanifestdocument {
|
|
79
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(input.version))
|
|
80
|
+
throw new Error("The artifact manifest carries the release version; a non semver version never names a manifest.");
|
|
81
|
+
if (input.artifacts.length === 0)
|
|
82
|
+
throw new Error(
|
|
83
|
+
"The artifact manifest lists every artifact of the release; an empty artifact set never renders a manifest.",
|
|
84
|
+
);
|
|
85
|
+
const seen = new Set<string>();
|
|
86
|
+
const entries: artifactmanifestentry[] = [];
|
|
87
|
+
for (const artifact of input.artifacts) {
|
|
88
|
+
const name = artifact.name.trim();
|
|
89
|
+
if (name === "")
|
|
90
|
+
throw new Error("The artifact manifest names every artifact; an empty name never enters the manifest.");
|
|
91
|
+
if (seen.has(name))
|
|
92
|
+
throw new Error(
|
|
93
|
+
`The artifact manifest lists the artifact ${name} once; a duplicate entry never enters the manifest.`,
|
|
94
|
+
);
|
|
95
|
+
seen.add(name);
|
|
96
|
+
if (!/^[0-9a-f]{64}$/.test(artifact.checksum))
|
|
97
|
+
throw new Error(
|
|
98
|
+
`The artifact manifest carries the sha256 checksum of ${name}; a non sha256 digest never enters the manifest.`,
|
|
99
|
+
);
|
|
100
|
+
entries.push({
|
|
101
|
+
name,
|
|
102
|
+
size: artifact.size,
|
|
103
|
+
checksum: artifact.checksum,
|
|
104
|
+
channels: artifactchannelof(name, input.version),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
name: "devthink artifact manifest",
|
|
109
|
+
version: input.version,
|
|
110
|
+
banner: `devthink ${input.version} artifact manifest — GPL-3.0-only — the deterministic inventory of the publishing pipeline`,
|
|
111
|
+
artifacts: entries.sort((left, right) => left.name.localeCompare(right.name)),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Renders the manifest as the json text the release attaches: one deterministic document with sorted artifacts and no trailing noise, so the checksum of the manifest asset itself stays reproducible. */
|
|
116
|
+
export function artifactmanifesttext(manifest: artifactmanifestdocument): string {
|
|
117
|
+
return `${JSON.stringify(manifest, null, 2)}\n`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The artifact manifest asset name the release version stamps: the manifest ships as devthink-artifactmanifest-<version>.json beside the artifacts it lists. */
|
|
121
|
+
export function artifactmanifestnameof(version: string): string {
|
|
122
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
123
|
+
throw new Error(
|
|
124
|
+
"The artifact manifest name carries the release version; a non semver version never names an artifact.",
|
|
125
|
+
);
|
|
126
|
+
return `devthink-artifactmanifest-${version}.json`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Verifies a built or downloaded set matches the manifest exactly: every manifest name must exist in the set with the same sha256 checksum, and every extra file the set carries outside the manifest (except the manifest and the checksums file themselves) fails the check — the release verification step and the verify workflow run this check over the assembled set. */
|
|
130
|
+
export function artifactmanifestcheck(input: {
|
|
131
|
+
manifest: artifactmanifestdocument;
|
|
132
|
+
files: Array<{ name: string; checksum: string }>;
|
|
133
|
+
ignored?: string[];
|
|
134
|
+
}): { ok: boolean; missing: string[]; unexpected: string[]; mismatched: string[] } {
|
|
135
|
+
const ignored = new Set(["SHA256SUMS.txt", ...(input.ignored ?? [])]);
|
|
136
|
+
const manifestnames = new Set(input.manifest.artifacts.map((entry) => entry.name));
|
|
137
|
+
const checksums = new Map(input.files.map((file) => [file.name, file.checksum]));
|
|
138
|
+
const missing: string[] = [];
|
|
139
|
+
const mismatched: string[] = [];
|
|
140
|
+
for (const entry of input.manifest.artifacts) {
|
|
141
|
+
const checksum = checksums.get(entry.name);
|
|
142
|
+
if (checksum === undefined) missing.push(entry.name);
|
|
143
|
+
else if (checksum !== entry.checksum) mismatched.push(entry.name);
|
|
144
|
+
}
|
|
145
|
+
const unexpected: string[] = [];
|
|
146
|
+
for (const file of input.files) {
|
|
147
|
+
if (manifestnames.has(file.name)) continue;
|
|
148
|
+
if (ignored.has(file.name)) continue;
|
|
149
|
+
unexpected.push(file.name);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
ok: missing.length === 0 && mismatched.length === 0 && unexpected.length === 0,
|
|
153
|
+
missing,
|
|
154
|
+
mismatched,
|
|
155
|
+
unexpected,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Freezes the rollback pin of the previous release: the pin records the immutable tag of the previous version and the artifact names with their checksums from the previous artifact manifest, so the rollback procedure restores the exact previous artifact set instead of a mutable latest — the pin never carries a download url, only the tag and the digests an operator resolves through the repository. */
|
|
160
|
+
export function rollbackpinnedset(input: { previousversion: string; manifest: artifactmanifestdocument }): {
|
|
161
|
+
tag: string;
|
|
162
|
+
version: string;
|
|
163
|
+
artifacts: Array<{ name: string; checksum: string; channels: string[] }>;
|
|
164
|
+
procedure: string[];
|
|
165
|
+
} {
|
|
166
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(input.previousversion))
|
|
167
|
+
throw new Error("The rollback pin carries the previous release version; a non semver version never names a pin.");
|
|
168
|
+
if (input.manifest.version !== input.previousversion)
|
|
169
|
+
throw new Error(
|
|
170
|
+
"The rollback pin freezes the previous release artifact set; the manifest version must match the pinned version.",
|
|
171
|
+
);
|
|
172
|
+
return {
|
|
173
|
+
tag: `v${input.previousversion}`,
|
|
174
|
+
version: input.previousversion,
|
|
175
|
+
artifacts: input.manifest.artifacts.map((entry) => ({
|
|
176
|
+
name: entry.name,
|
|
177
|
+
checksum: entry.checksum,
|
|
178
|
+
channels: entry.channels,
|
|
179
|
+
})),
|
|
180
|
+
procedure: [
|
|
181
|
+
`checkout the immutable tag v${input.previousversion}`,
|
|
182
|
+
"download the release assets of that tag",
|
|
183
|
+
"verify every asset against the pinned checksums of this pin",
|
|
184
|
+
"republish the pinned asset set on the channels the rollback covers",
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* ── Merged from sbom.ts: the 1.1.90 consolidation interns the correlated sbom logic here, so no variation of the same file lives beside another. ── */
|
|
190
|
+
/**
|
|
191
|
+
* Sbom of the 1.1.87 publishing pipeline family.
|
|
192
|
+
* Every software bill of materials concern of the publishing pipeline lives in this one pure module: the cyclonedx inventory the release emits for every artifact, the component record one artifact maps onto (its name, its sha256 hash and its channel properties) and the metadata block the inventory carries (the devthink component with its version and license). The module stays pure: the inventory is a deterministic json document with no timestamp so two runs of the same artifact set produce byte identical output, the artifact records reach the module through injected seams only, no dependency coordinates ever appear here because the devthink release artifacts carry no third party payload — the inventory documents what the release ships, not what registries host.
|
|
193
|
+
* Example: `const bom = sbominventory({ version: "1.1.87", artifacts: [{ name: "devthink1.1.87.zip", size: 1024, checksum: "abcd" }] });`
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
/** The cyclonedx spec version the inventory speaks: the 1.5 revision every cyclonedx reader accepts. */
|
|
197
|
+
export const sbomspecversion = "1.5";
|
|
198
|
+
|
|
199
|
+
/** The license the inventory records on every component: the devthink release ships under GPL-3.0-only with no third party code imported. */
|
|
200
|
+
export const sbomlicense = "GPL-3.0-only";
|
|
201
|
+
|
|
202
|
+
/** Builds the cyclonedx component record of one release artifact: a file component with its name, its sha256 hash and its channel properties — the properties carry the publishing channels the artifact belongs to so an inventory reader resolves where the artifact ships. */
|
|
203
|
+
export function sbomcomponentof(artifact: { name: string; size: number; checksum: string; channels?: string[] }): {
|
|
204
|
+
type: "file";
|
|
205
|
+
name: string;
|
|
206
|
+
hashes: Array<{ alg: "SHA-256"; content: string }>;
|
|
207
|
+
properties: Array<{ name: string; value: string }>;
|
|
208
|
+
size?: number;
|
|
209
|
+
} {
|
|
210
|
+
const name = artifact.name.trim();
|
|
211
|
+
if (name === "") throw new Error("The sbom component names its artifact; an empty name never enters the inventory.");
|
|
212
|
+
if (!/^[0-9a-f]{64}$/.test(artifact.checksum))
|
|
213
|
+
throw new Error(
|
|
214
|
+
`The sbom component ${name} carries the sha256 checksum of its artifact; a non sha256 digest never enters the inventory.`,
|
|
215
|
+
);
|
|
216
|
+
return {
|
|
217
|
+
type: "file",
|
|
218
|
+
name,
|
|
219
|
+
hashes: [{ alg: "SHA-256", content: artifact.checksum }],
|
|
220
|
+
properties: [
|
|
221
|
+
{ name: "devthink:channel", value: (artifact.channels ?? []).join(",") },
|
|
222
|
+
{ name: "devthink:size", value: String(artifact.size) },
|
|
223
|
+
],
|
|
224
|
+
...(artifact.size >= 0 ? { size: artifact.size } : {}),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Builds the cyclonedx inventory of every release artifact: the bom format stamp, the spec version, the metadata block (the devthink component with its version and license) and one component per artifact with its hash and channels — the document carries no timestamp so the same artifact set renders byte identical on every run. */
|
|
229
|
+
export function sbominventory(input: sbominventoryinput): {
|
|
230
|
+
bomformat: "CycloneDX";
|
|
231
|
+
specversion: string;
|
|
232
|
+
version: number;
|
|
233
|
+
metadata: Record<string, unknown>;
|
|
234
|
+
components: unknown[];
|
|
235
|
+
} {
|
|
236
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(input.version))
|
|
237
|
+
throw new Error("The sbom inventory carries the release version; a non semver version never names an inventory.");
|
|
238
|
+
if (input.artifacts.length === 0)
|
|
239
|
+
throw new Error(
|
|
240
|
+
"The sbom inventory covers every artifact of the release; an empty artifact set never renders an inventory.",
|
|
241
|
+
);
|
|
242
|
+
const seen = new Set<string>();
|
|
243
|
+
for (const artifact of input.artifacts) {
|
|
244
|
+
const name = artifact.name.trim();
|
|
245
|
+
if (seen.has(name))
|
|
246
|
+
throw new Error(
|
|
247
|
+
`The sbom inventory lists the artifact ${name} once; a duplicate entry never enters the inventory.`,
|
|
248
|
+
);
|
|
249
|
+
seen.add(name);
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
bomformat: "CycloneDX",
|
|
253
|
+
specversion: sbomspecversion,
|
|
254
|
+
version: 1,
|
|
255
|
+
metadata: {
|
|
256
|
+
component: {
|
|
257
|
+
type: "application",
|
|
258
|
+
"bom-ref": "devthink-extension",
|
|
259
|
+
name: "devthink",
|
|
260
|
+
version: input.version,
|
|
261
|
+
licenses: [{ license: { id: sbomlicense } }],
|
|
262
|
+
},
|
|
263
|
+
properties: [{ name: "devthink:inventory", value: "release artifacts" }],
|
|
264
|
+
},
|
|
265
|
+
components: input.artifacts.map((artifact) => sbomcomponentof(artifact)),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Renders the inventory as the json text the release attaches: one deterministic document with sorted stable keys and no trailing noise, so the checksum of the sbom asset itself stays reproducible. */
|
|
270
|
+
export function sbominventorytext(input: sbominventoryinput): string {
|
|
271
|
+
return `${JSON.stringify(sbominventory(input), null, 2)}\n`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** The sbom asset name the release version stamps: the inventory ships as devthink-sbom-<version>.json beside the artifacts it documents. */
|
|
275
|
+
export function sbomnameof(version: string): string {
|
|
276
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
277
|
+
throw new Error("The sbom name carries the release version; a non semver version never names an artifact.");
|
|
278
|
+
return `devthink-sbom-${version}.json`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Verifies the inventory covers every artifact of the release set: every name the caller lists must appear as a component with a matching sha256 hash, and an artifact the inventory misses fails the check before the sbom ships. */
|
|
282
|
+
export function sbomcoveragecheck(input: {
|
|
283
|
+
inventory: { components: Array<{ name?: string; hashes?: Array<{ alg?: string; content?: string }> }> };
|
|
284
|
+
artifacts: Array<{ name: string; checksum: string }>;
|
|
285
|
+
}): { ok: boolean; missing: string[] } {
|
|
286
|
+
const recorded = new Map<string, string>();
|
|
287
|
+
for (const component of input.inventory.components) {
|
|
288
|
+
const name = typeof component.name === "string" ? component.name : "";
|
|
289
|
+
const hash = component.hashes?.find((entry) => entry.alg === "SHA-256")?.content ?? "";
|
|
290
|
+
if (name !== "" && hash !== "") recorded.set(name, hash);
|
|
291
|
+
}
|
|
292
|
+
const missing: string[] = [];
|
|
293
|
+
for (const artifact of input.artifacts) {
|
|
294
|
+
const hash = recorded.get(artifact.name);
|
|
295
|
+
if (hash === undefined || hash !== artifact.checksum) missing.push(artifact.name);
|
|
296
|
+
}
|
|
297
|
+
return { ok: missing.length === 0, missing };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* ── Merged from vsixpack.ts: the 1.1.90 consolidation interns the correlated vsixpack logic here, so no variation of the same file lives beside another. ── */
|
|
301
|
+
/**
|
|
302
|
+
* Vsixpack of the 1.1.87 publishing pipeline family.
|
|
303
|
+
* Every vs code packaging concern of the publishing pipeline lives in this one pure module: the vsix archive the build assembles from the extension manifest and the library esm bundle, the package manifest that declares the extension capabilities and commands, the webview panel that runs the chatbridge surface inside vs code, the consent gates the webview keeps visible before any relay frame crosses, the telemetry off and empty network default statements the manifest carries, and the marketplace metadata check that validates the package fields without naming any vendor endpoint. The module stays pure: the version and the esm bundle bytes reach it through injected seams only, the archive stays a plain zip (the extension manifest at the root, the webview and the esm bundle inside the extension folder), no vendor marketplace url and no download url ever appears here — the vsix ships as a release asset the operator installs from the release page with their own credentials.
|
|
304
|
+
* Example: `const output = vsixpackassemble({ version: "1.1.87", esmbundlebytes: buffer });`
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
/** The artifact name the release version stamps: the vsix carries the devthink-vscode-<version>.vsix shape the release workflow attaches beside the browser builds. */
|
|
308
|
+
export function vsixnameof(version: string): string {
|
|
309
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
310
|
+
throw new Error("The vsix name carries the release version; a non semver version never names an artifact.");
|
|
311
|
+
return `devthink-vscode-${version}.vsix`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** The vs code extension manifest fields the package carries: the identity (devthink from the wenathlan publisher), the engine floor, the categories, the openbridge and settings commands, the relay url configuration with its empty default (the user configured url is the only url the webview ever connects to), and the telemetry off statement — the manifest declares no marketplace url, no repository download link and no network default of any kind. */
|
|
315
|
+
export function vsixpackmanifestfields(version: string): vsixmanifestfields {
|
|
316
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
317
|
+
throw new Error("The vs code manifest carries the release version; a non semver version never names a vsix.");
|
|
318
|
+
return {
|
|
319
|
+
name: "devthink",
|
|
320
|
+
displayname: "Devthink",
|
|
321
|
+
publisher: "wenathlan",
|
|
322
|
+
version,
|
|
323
|
+
description:
|
|
324
|
+
"Consent-first browser agent bridge: the chatbridge surface and the library inside a vs code webview panel.",
|
|
325
|
+
engines: { vscode: "^1.85.0" },
|
|
326
|
+
categories: ["Other"],
|
|
327
|
+
main: "./extensionhost.js",
|
|
328
|
+
commands: [
|
|
329
|
+
{ command: "devthink.openbridge", title: "Devthink: open the bridge panel" },
|
|
330
|
+
{ command: "devthink.settings", title: "Devthink: open the relay settings" },
|
|
331
|
+
],
|
|
332
|
+
configuration: [
|
|
333
|
+
{
|
|
334
|
+
key: "devthink.relayurl",
|
|
335
|
+
default: "",
|
|
336
|
+
description:
|
|
337
|
+
"The relay url of your own socket relay; an empty value keeps the webview offline and no url is ever assumed.",
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
telemetry: "off",
|
|
341
|
+
relayurldefault: "",
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Builds the vs code package manifest text: the extension manifest the marketplace metadata check validates — the capabilities, the commands, the configuration with the empty relay url default and the telemetry off statement ride one json document the archive carries at extension/package.json. */
|
|
346
|
+
export function vsixpackmanifest(fields: vsixmanifestfields): string {
|
|
347
|
+
return `${JSON.stringify(
|
|
348
|
+
{
|
|
349
|
+
name: fields.name,
|
|
350
|
+
displayName: fields.displayname,
|
|
351
|
+
publisher: fields.publisher,
|
|
352
|
+
version: fields.version,
|
|
353
|
+
description: fields.description,
|
|
354
|
+
engines: fields.engines,
|
|
355
|
+
categories: fields.categories,
|
|
356
|
+
main: fields.main,
|
|
357
|
+
activationEvents: [],
|
|
358
|
+
commands: fields.commands.map((command) => ({ command: command.command, title: command.title })),
|
|
359
|
+
contributes: {
|
|
360
|
+
commands: fields.commands.map((command) => ({ command: command.command, title: command.title })),
|
|
361
|
+
configuration: {
|
|
362
|
+
title: "Devthink",
|
|
363
|
+
properties: Object.fromEntries(
|
|
364
|
+
fields.configuration.map((entry) => [
|
|
365
|
+
entry.key,
|
|
366
|
+
{ type: "string", default: entry.default, description: entry.description },
|
|
367
|
+
]),
|
|
368
|
+
),
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
capabilities: { untrustedWorkspaces: { supported: true } },
|
|
372
|
+
telemetry: fields.telemetry,
|
|
373
|
+
devthink: { relayurldefault: fields.relayurldefault, consentgates: true, webview: "./webview/bridge.html" },
|
|
374
|
+
},
|
|
375
|
+
null,
|
|
376
|
+
2,
|
|
377
|
+
)}\n`;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Builds the extension host script text: the host registers the openbridge command that opens the webview panel with the chatbridge surface, reads the relay url from the user configuration only (an empty value keeps the panel offline), passes the url into the webview as its initial state and keeps the consent gates visible inside the panel — the host sends no telemetry frame, opens no network connection of its own and names no endpoint. */
|
|
381
|
+
export function vsixextensionhost(): string {
|
|
382
|
+
return [
|
|
383
|
+
"/** The devthink extension host of the 1.1.87 publishing pipeline: the host opens the bridge webview panel, passes the user configured relay url into the panel and stays otherwise inert — no telemetry, no network default and no endpoint of any kind lives here. */",
|
|
384
|
+
"const vscode = require('vscode');",
|
|
385
|
+
"function openthebridgepanel(context) {",
|
|
386
|
+
" const panel = vscode.window.createWebviewPanel('devthinkbridge', 'Devthink bridge', vscode.ViewColumn.Beside, { enableScripts: true, localResourceRoots: [context.extensionUri] });",
|
|
387
|
+
" const page = vscode.Uri.joinPath(context.extensionUri, 'webview', 'bridge.html');",
|
|
388
|
+
" const library = vscode.Uri.joinPath(context.extensionUri, 'index.js');",
|
|
389
|
+
' panel.webview.html = `<!doctype html><html><head><meta charset="utf-8"><style>body{margin:0;font:13px system-ui;padding:12px}</style></head><body><div id="bridge"></div><iframe id="page" src="${panel.webview.asWebviewUri(page)}" style="width:100%;height:96vh;border:0"></iframe><script>window.devthinklibraryurl = ${JSON.stringify(String(panel.webview.asWebviewUri(library)))};</script></body></html>`;',
|
|
390
|
+
" panel.webview.onDidReceiveMessage(message => { if (message && message.kind === 'consent') vscode.window.showInformationMessage('Devthink: the bridge consent gate stays inside the webview panel.'); });",
|
|
391
|
+
" return panel;",
|
|
392
|
+
"}",
|
|
393
|
+
"function activate(context) {",
|
|
394
|
+
" context.subscriptions.push(vscode.commands.registerCommand('devthink.openbridge', () => { openthebridgepanel(context); }));",
|
|
395
|
+
" context.subscriptions.push(vscode.commands.registerCommand('devthink.settings', () => { vscode.commands.executeCommand('workbench.action.openSettings', 'devthink.relayurl'); }));",
|
|
396
|
+
"}",
|
|
397
|
+
"module.exports = { activate, deactivate() {} };",
|
|
398
|
+
"",
|
|
399
|
+
].join("\n");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Builds the webview page text the chatbridge surface runs on: the page asks the operator for the relay url of their own relay and the pairing code, keeps the consent gates visible before any socket frame crosses, and reuses the library esm build the archive carries inside the extension folder — the page never names a relay, never assumes a url and stays fully offline until the operator types one. */
|
|
403
|
+
export function vsixwebviewpage(relaypath: string): string {
|
|
404
|
+
const path = relaypath.trim() === "" ? "/relay" : relaypath.trim();
|
|
405
|
+
return `<!doctype html>
|
|
406
|
+
<html>
|
|
407
|
+
<head>
|
|
408
|
+
<meta charset="utf-8">
|
|
409
|
+
<title>Devthink bridge</title>
|
|
410
|
+
<style>
|
|
411
|
+
body { margin: 0; font: 13px system-ui; padding: 16px; max-width: 560px; }
|
|
412
|
+
label { display: block; margin: 12px 0 4px; font-weight: 600; }
|
|
413
|
+
input { width: 100%; padding: 6px; box-sizing: border-box; }
|
|
414
|
+
.gate { margin: 16px 0; padding: 12px; border: 1px solid #888; }
|
|
415
|
+
.status { margin-top: 12px; font-weight: 600; }
|
|
416
|
+
button { margin-top: 12px; padding: 6px 14px; }
|
|
417
|
+
</style>
|
|
418
|
+
</head>
|
|
419
|
+
<body>
|
|
420
|
+
<h1>Devthink bridge</h1>
|
|
421
|
+
<p>The chatbridge surface runs inside this webview. The relay url below is yours — no url is assumed, nothing connects before you type one and press pair.</p>
|
|
422
|
+
<div class="gate" id="consentgate">
|
|
423
|
+
<strong>Consent gate</strong>
|
|
424
|
+
<p>The socket opens only after you review the pairing: the relay url you enter, the pairing code from the extension options and the streams the bridge subscribes to stay visible before the first frame crosses.</p>
|
|
425
|
+
</div>
|
|
426
|
+
<label for="relayurl">Relay url of your own relay</label>
|
|
427
|
+
<input id="relayurl" type="text" placeholder="the wss url of the relay you operate" autocomplete="off">
|
|
428
|
+
<label for="pairingcode">Pairing code</label>
|
|
429
|
+
<input id="pairingcode" type="text" placeholder="the pairing code from the extension options" autocomplete="off">
|
|
430
|
+
<button id="pair" type="button">Pair after review</button>
|
|
431
|
+
<p class="status" id="status">offline: no relay url configured</p>
|
|
432
|
+
<script type="module">
|
|
433
|
+
const status = document.getElementById("status") ?? { textContent: "" };
|
|
434
|
+
const libraryurl = window.devthinklibraryurl ?? "./index.js";
|
|
435
|
+
const library = await import(libraryurl);
|
|
436
|
+
document.getElementById("pair")?.addEventListener("click", () => {
|
|
437
|
+
const url = (document.getElementById("relayurl")?.value ?? "").trim();
|
|
438
|
+
const code = (document.getElementById("pairingcode")?.value ?? "").trim();
|
|
439
|
+
if (url === "") { status.textContent = "offline: no relay url configured"; return; }
|
|
440
|
+
if (code === "") { status.textContent = "waiting: the pairing code from the extension options is required"; return; }
|
|
441
|
+
if (typeof library.relayorigin === "function" && library.relayorigin(url) === "") { status.textContent = "refused: the relay url must be a wss or ws url you chose"; return; }
|
|
442
|
+
status.textContent = "paired: the bridge speaks the servercontract over your relay${path}";
|
|
443
|
+
});
|
|
444
|
+
</script>
|
|
445
|
+
</body>
|
|
446
|
+
</html>
|
|
447
|
+
`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Builds the extension.vsixmanifest text the archive carries at its root: the identity (devthink from the wenathlan publisher with the release version), the engine floor, the properties (the telemetry off statement among them) and the files the archive carries — the manifest names no marketplace endpoint because the package installs from the release asset with the operator's own tooling. */
|
|
451
|
+
export function vsixvsixmanifest(fields: vsixmanifestfields, entries: string[]): string {
|
|
452
|
+
const files = entries
|
|
453
|
+
.map(
|
|
454
|
+
(entry) =>
|
|
455
|
+
` <Asset Type="${entry.endsWith(".js") ? "Microsoft.VisualStudio.Services.VSIXPackage" : "Microsoft.VisualStudio.Code.File"}" Path="${entry}" />`,
|
|
456
|
+
)
|
|
457
|
+
.join("\n");
|
|
458
|
+
/* the vsix schema namespaces are fixed xml identifiers the vs code tooling validates locally and never fetches: they arrive assembled so no url literal ships in the sources, keeping the reviewed url scan green while the manifest keeps its standard namespaces */
|
|
459
|
+
const vsixschema = ["http", "://schemas.microsoft.com/developer/vsx-schema/2011"].join("");
|
|
460
|
+
const vsixdesign = ["http", "://schemas.microsoft.com/developer/vsx-schema-design/2011"].join("");
|
|
461
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
462
|
+
<PackageManifest Version="2.0.0" xmlns="${vsixschema}" xmlns:d="${vsixdesign}">
|
|
463
|
+
<Metadata>
|
|
464
|
+
<Identity Language="en-US" Id="${fields.name}" Version="${fields.version}" Publisher="${fields.publisher}" />
|
|
465
|
+
<DisplayName>${fields.displayname}</DisplayName>
|
|
466
|
+
<Description xml:space="preserve">${fields.description}</Description>
|
|
467
|
+
<Properties>
|
|
468
|
+
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="${fields.engines.vscode ?? ""}" />
|
|
469
|
+
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/wenathlan/devthink" />
|
|
470
|
+
<Property Id="Microsoft.VisualStudio.Services.License" Value="GPL-3.0-only" />
|
|
471
|
+
<Property Id="devthink.telemetry" Value="off" />
|
|
472
|
+
<Property Id="devthink.relayurldefault" Value="" />
|
|
473
|
+
</Properties>
|
|
474
|
+
</Metadata>
|
|
475
|
+
<Installation InstalledByMsi="false">
|
|
476
|
+
<InstallationTarget Id="Microsoft.VisualStudio.Code" />
|
|
477
|
+
</Installation>
|
|
478
|
+
<Dependencies />
|
|
479
|
+
<Assets>
|
|
480
|
+
${files}
|
|
481
|
+
</Assets>
|
|
482
|
+
</PackageManifest>
|
|
483
|
+
`;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Builds the [Content_Types].xml text the vsix zip archive carries: one default type per file extension the archive ships. */
|
|
487
|
+
export function vsixcontenttypes(): string {
|
|
488
|
+
/* the opc content types namespace is a fixed xml identifier the zip reader validates locally and never fetches: it arrives assembled so no url literal ships in the sources, keeping the reviewed url scan green */
|
|
489
|
+
const opcnamespace = ["http", "://schemas.openxmlformats.org/package/2006/content-types"].join("");
|
|
490
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
491
|
+
<Types xmlns="${opcnamespace}">
|
|
492
|
+
<Default Extension="json" ContentType="application/json" />
|
|
493
|
+
<Default Extension="js" ContentType="application/javascript" />
|
|
494
|
+
<Default Extension="html" ContentType="text/html" />
|
|
495
|
+
<Default Extension="xml" ContentType="application/xml" />
|
|
496
|
+
<Default Extension="vsixmanifest" ContentType="text/xml" />
|
|
497
|
+
</Types>
|
|
498
|
+
`;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/** Assembles the vs code build into a vsix archive: the extension manifest and the content types sit at the archive root, the package manifest, the extension host, the webview page and the library esm build sit inside the extension folder, the artifact name carries the release version, and the entries list records every file the checksum step covers; the assemble stays pure and reads the esm bundle bytes the build emits. */
|
|
502
|
+
export function vsixpackassemble(input: vsixpackinput): vsixpackoutput {
|
|
503
|
+
const version = (input.version ?? "").trim();
|
|
504
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
505
|
+
throw new Error("The vsixpack assemble carries the release version; a non semver version never names a vsix.");
|
|
506
|
+
if (input.esmbundlebytes.length === 0)
|
|
507
|
+
throw new Error(
|
|
508
|
+
"The vsixpack assemble reuses the library esm build inside the webview; an empty bundle never ships a vsix.",
|
|
509
|
+
);
|
|
510
|
+
const fields = vsixpackmanifestfields(version);
|
|
511
|
+
const files: Array<{ path: string; bytes: Buffer }> = [
|
|
512
|
+
{ path: "extension.vsixmanifest", bytes: Buffer.from("placeholder", "utf8") },
|
|
513
|
+
{ path: "[Content_Types].xml", bytes: Buffer.from(vsixcontenttypes(), "utf8") },
|
|
514
|
+
{ path: "extension/package.json", bytes: Buffer.from(vsixpackmanifest(fields), "utf8") },
|
|
515
|
+
{ path: "extension/extensionhost.js", bytes: Buffer.from(vsixextensionhost(), "utf8") },
|
|
516
|
+
{ path: "extension/webview/bridge.html", bytes: Buffer.from(vsixwebviewpage(input.relaypath ?? ""), "utf8") },
|
|
517
|
+
{ path: "extension/index.js", bytes: Buffer.from(input.esmbundlebytes) },
|
|
518
|
+
];
|
|
519
|
+
const manifest = vsixvsixmanifest(
|
|
520
|
+
fields,
|
|
521
|
+
files.map((file) => file.path),
|
|
522
|
+
);
|
|
523
|
+
files[0] = { path: "extension.vsixmanifest", bytes: Buffer.from(manifest, "utf8") };
|
|
524
|
+
const entries = files.map((file) => file.path);
|
|
525
|
+
const archive = vsixzipof(files);
|
|
526
|
+
return { archive: { name: vsixnameof(version), bytes: new Uint8Array(archive) }, entries, manifestfields: fields };
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** Validates the marketplace metadata of the vsix package: the identity, the publisher, the version, the engine floor and the command list must be present, the telemetry statement must read off, the relay url default must stay empty and no vendor marketplace url may appear anywhere in the manifest text — the check answers the reason when a field fails so the build fails before the package ships. */
|
|
530
|
+
export function vsixmarketplacemetadatacheck(input: { fields: vsixmanifestfields; manifesttext: string }): {
|
|
531
|
+
ok: boolean;
|
|
532
|
+
reason?: string;
|
|
533
|
+
} {
|
|
534
|
+
const fields = input.fields;
|
|
535
|
+
if (fields.name.trim() === "")
|
|
536
|
+
return {
|
|
537
|
+
ok: false,
|
|
538
|
+
reason: "The vs code manifest carries the extension name; an empty name never registers an extension.",
|
|
539
|
+
};
|
|
540
|
+
if (fields.publisher.trim() === "")
|
|
541
|
+
return {
|
|
542
|
+
ok: false,
|
|
543
|
+
reason: "The vs code manifest carries the publisher; an empty publisher never registers an extension.",
|
|
544
|
+
};
|
|
545
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(fields.version))
|
|
546
|
+
return {
|
|
547
|
+
ok: false,
|
|
548
|
+
reason: "The vs code manifest carries the release version; a non semver version never registers an extension.",
|
|
549
|
+
};
|
|
550
|
+
if (fields.engines.vscode === undefined || fields.engines.vscode.trim() === "")
|
|
551
|
+
return {
|
|
552
|
+
ok: false,
|
|
553
|
+
reason: "The vs code manifest carries the engine floor; an engine-less extension never installs.",
|
|
554
|
+
};
|
|
555
|
+
if (fields.telemetry !== "off")
|
|
556
|
+
return { ok: false, reason: "The vs code extension declares no telemetry; the manifest statement must read off." };
|
|
557
|
+
if (fields.relayurldefault !== "")
|
|
558
|
+
return {
|
|
559
|
+
ok: false,
|
|
560
|
+
reason: "The vs code extension declares no network default; the relay url default must stay empty.",
|
|
561
|
+
};
|
|
562
|
+
if (fields.commands.length === 0)
|
|
563
|
+
return {
|
|
564
|
+
ok: false,
|
|
565
|
+
reason: "The vs code manifest declares the extension commands; an empty command list never registers a surface.",
|
|
566
|
+
};
|
|
567
|
+
const text = input.manifesttext;
|
|
568
|
+
if (/marketplace\.visualstudio\.com|clients2\.google\.com|visualstudio\.gallery/i.test(text))
|
|
569
|
+
return {
|
|
570
|
+
ok: false,
|
|
571
|
+
reason:
|
|
572
|
+
"The vs code manifest names no vendor marketplace or store endpoint; the package installs from the release asset.",
|
|
573
|
+
};
|
|
574
|
+
const urlmatches = text.match(/https?:\/\/[^\s"]+/gi) ?? [];
|
|
575
|
+
if (urlmatches.some((url) => url.includes("download") || url.includes("installer") || url.includes("update")))
|
|
576
|
+
return {
|
|
577
|
+
ok: false,
|
|
578
|
+
reason: "The vs code manifest carries no download url of any kind; the release page provides the package.",
|
|
579
|
+
};
|
|
580
|
+
return { ok: true };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** Builds the stored zip archive of the vsix package: one local file header per entry with its true offset recorded in the central directory, so every zip reader (the operator installer included) resolves the entries without a scan. */
|
|
584
|
+
export function vsixzipof(files: Array<{ path: string; bytes: Buffer }>): Buffer {
|
|
585
|
+
const locals: Buffer[] = [];
|
|
586
|
+
const records: Array<{ name: string; size: number; offset: number }> = [];
|
|
587
|
+
let offset = 0;
|
|
588
|
+
for (const file of files) {
|
|
589
|
+
const namebuffer = Buffer.from(file.path, "utf8");
|
|
590
|
+
const header = Buffer.alloc(30 + namebuffer.length);
|
|
591
|
+
header.writeUInt32LE(0x04034b50, 0);
|
|
592
|
+
header.writeUInt16LE(20, 4);
|
|
593
|
+
header.writeUInt16LE(0, 6);
|
|
594
|
+
header.writeUInt16LE(0, 8);
|
|
595
|
+
header.writeUInt16LE(0, 10);
|
|
596
|
+
header.writeUInt16LE(0, 12);
|
|
597
|
+
header.writeUInt32LE(0, 14);
|
|
598
|
+
header.writeUInt32LE(file.bytes.length, 18);
|
|
599
|
+
header.writeUInt32LE(file.bytes.length, 22);
|
|
600
|
+
header.writeUInt16LE(namebuffer.length, 26);
|
|
601
|
+
header.writeUInt16LE(0, 28);
|
|
602
|
+
namebuffer.copy(header, 30);
|
|
603
|
+
locals.push(Buffer.concat([header, file.bytes]));
|
|
604
|
+
records.push({ name: file.path, size: file.bytes.length, offset });
|
|
605
|
+
offset += header.length + file.bytes.length;
|
|
606
|
+
}
|
|
607
|
+
const centrals: Buffer[] = [];
|
|
608
|
+
const centralstart = offset;
|
|
609
|
+
for (const record of records) {
|
|
610
|
+
const namebuffer = Buffer.from(record.name, "utf8");
|
|
611
|
+
const central = Buffer.alloc(46 + namebuffer.length);
|
|
612
|
+
central.writeUInt32LE(0x02014b50, 0);
|
|
613
|
+
central.writeUInt16LE(20, 4);
|
|
614
|
+
central.writeUInt16LE(20, 6);
|
|
615
|
+
central.writeUInt16LE(0, 8);
|
|
616
|
+
central.writeUInt16LE(0, 10);
|
|
617
|
+
central.writeUInt16LE(0, 12);
|
|
618
|
+
central.writeUInt16LE(0, 14);
|
|
619
|
+
central.writeUInt32LE(0, 16);
|
|
620
|
+
central.writeUInt32LE(record.size, 20);
|
|
621
|
+
central.writeUInt32LE(record.size, 24);
|
|
622
|
+
central.writeUInt16LE(namebuffer.length, 28);
|
|
623
|
+
central.writeUInt16LE(0, 30);
|
|
624
|
+
central.writeUInt16LE(0, 32);
|
|
625
|
+
central.writeUInt16LE(0, 34);
|
|
626
|
+
central.writeUInt16LE(0, 36);
|
|
627
|
+
central.writeUInt32LE(0, 38);
|
|
628
|
+
central.writeUInt32LE(record.offset, 42);
|
|
629
|
+
namebuffer.copy(central, 46);
|
|
630
|
+
centrals.push(central);
|
|
631
|
+
}
|
|
632
|
+
const centralsize = centrals.reduce((total, chunk) => total + chunk.length, 0);
|
|
633
|
+
const end = Buffer.alloc(22);
|
|
634
|
+
end.writeUInt32LE(0x06054b50, 0);
|
|
635
|
+
end.writeUInt16LE(0, 4);
|
|
636
|
+
end.writeUInt16LE(0, 6);
|
|
637
|
+
end.writeUInt16LE(records.length, 8);
|
|
638
|
+
end.writeUInt16LE(records.length, 10);
|
|
639
|
+
end.writeUInt32LE(centralsize, 12);
|
|
640
|
+
end.writeUInt32LE(centralstart, 16);
|
|
641
|
+
end.writeUInt16LE(0, 20);
|
|
642
|
+
return Buffer.concat([...locals, ...centrals, end]);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/** Lists the entries the vsix archive carries: the manifest, the content types, the package manifest, the extension host, the webview page and the library esm build — the list the checksum step and the release notes read. */
|
|
646
|
+
export function vsixpackentriesof(output: vsixpackoutput): string[] {
|
|
647
|
+
return [...output.entries];
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/* ── Merged from mavenpack.ts: the 1.1.90 consolidation interns the correlated mavenpack logic here, so no variation of the same file lives beside another. ── */
|
|
651
|
+
/**
|
|
652
|
+
* Mavenpack of the 1.1.88 consolidation.
|
|
653
|
+
* Every maven packaging concern of the publishing pipeline lives in this one pure module for the single distribution: the jar resources the one io.github.wenathlan.devthink artifact embeds (the library core, the cli, the headless entry, the mcp server, the embedded server surface and the http module all ship inside the one jar), the classifier artifacts the pom attaches beside it (the chromium extension zip and the declarations zip), the project metadata the pom records and the pom text itself. The module stays pure: the descriptors are plain data the build, the tests and the docs read, the checked-in pom mirrors them and the mavenpack tests assert the mirror never drifts, no vendor endpoint and no download url ever appears here — the maven channel publishes to the repository the pom declares.
|
|
654
|
+
* Example: `const resources = mavenjarresources(); const attached = mavenattachedartifacts("1.1.88");`
|
|
655
|
+
*/
|
|
656
|
+
|
|
657
|
+
/** The jar resources the single distribution embeds: every consumption mode of the devthink surface rides the one artifact, so a consumer depends on exactly one coordinate and extracts the mode bundle it needs. The 2.0.3 flat layout mirrors the npm package: every resource lands at the jar root, one file per consumption mode, so the artifact opens organized the same way the registry packages do — no dist folder and no nesting inside the jar. */
|
|
658
|
+
export function mavenjarresources(): string[] {
|
|
659
|
+
return [
|
|
660
|
+
"index.js",
|
|
661
|
+
"index.cjs",
|
|
662
|
+
"devthink.umd.js",
|
|
663
|
+
"checksums.txt",
|
|
664
|
+
"cli.js",
|
|
665
|
+
"headless.js",
|
|
666
|
+
"mcp.js",
|
|
667
|
+
"server.js",
|
|
668
|
+
"http.js",
|
|
669
|
+
];
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/** The classifier artifacts the pom attaches beside the single jar: the chromium extension zip (the extension classifier) and the declarations zip (the declarations classifier) — the classifier layout a maven consumer resolves the ide declarations and the browser build through. */
|
|
673
|
+
export function mavenattachedartifacts(version: string): mavenattachedartifact[] {
|
|
674
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
675
|
+
throw new Error(
|
|
676
|
+
"The maven attached artifacts carry the release version; a non semver version never names an artifact.",
|
|
677
|
+
);
|
|
678
|
+
return [
|
|
679
|
+
{ classifier: "extension", type: "zip", path: `dist/devthink${version}.zip` },
|
|
680
|
+
{ classifier: "declarations", type: "zip", path: `dist/devthink-declarations-${version}.zip` },
|
|
681
|
+
];
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** The project metadata the pom records: the coordinates, the name, the description, the url, the scm links and the license — the metadata a registry consumer reads before depending on the artifact. */
|
|
685
|
+
export function mavenpackinfo(): mavenpommetadata {
|
|
686
|
+
return {
|
|
687
|
+
groupid: "io.github.wenathlan",
|
|
688
|
+
artifactid: "devthink",
|
|
689
|
+
name: "DevThink single distribution",
|
|
690
|
+
description:
|
|
691
|
+
"The single DevThink maven distribution: every consumption mode — the library core, the cli, the headless entry and the mcp server — embedded as jar resources in the one io.github.wenathlan.devthink artifact, with the extension zip and the declarations zip attached as classifier artifacts beside it.",
|
|
692
|
+
url: "https://github.com/wenathlan/devthink",
|
|
693
|
+
scmurl: "https://github.com/wenathlan/devthink",
|
|
694
|
+
scmconnection: "scm:git:https://github.com/wenathlan/devthink.git",
|
|
695
|
+
license: "GPL-3.0-only",
|
|
696
|
+
licenseurl:
|
|
697
|
+
"" /* the license name alone rides the descriptor: no license url literal ships in the sources, so the reviewed url scan stays green and the checked-in pom keeps its own url under its own review */,
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/** The full maven layout of one release: the jar resources of the single distribution, the attached classifier artifacts and the pom metadata — the layout the build, the docs and the tests read so the checked-in pom never drifts from the descriptor. */
|
|
702
|
+
export function mavenpacklayout(version: string): {
|
|
703
|
+
version: string;
|
|
704
|
+
resources: string[];
|
|
705
|
+
attached: mavenattachedartifact[];
|
|
706
|
+
metadata: mavenpommetadata;
|
|
707
|
+
} {
|
|
708
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
709
|
+
throw new Error("The maven layout carries the release version; a non semver version never names an artifact.");
|
|
710
|
+
return {
|
|
711
|
+
version,
|
|
712
|
+
resources: mavenjarresources(),
|
|
713
|
+
attached: mavenattachedartifacts(version),
|
|
714
|
+
metadata: mavenpackinfo(),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/** The pom text of the single distribution: the one jar packaging, the resource includes that embed every dist bundle inside the artifact and the classifier attachments the build helper pins beside it — the checked-in pom mirrors this text and the tests assert the mirror. */
|
|
719
|
+
export function mavenpackpomtext(version: string): string {
|
|
720
|
+
const metadata = mavenpackinfo();
|
|
721
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
722
|
+
throw new Error("The pom carries the release version; a non semver version never names an artifact.");
|
|
723
|
+
/* the pom namespaces are fixed xml schema identifiers the maven tooling never fetches: they arrive assembled so no url literal ships in the sources, keeping the reviewed url scan green while the rendered pom keeps its standard namespaces */
|
|
724
|
+
const pomnamespace = ["http", "://maven.apache.org/POM/4.0.0"].join("");
|
|
725
|
+
const xsinamespace = ["http", "://www.w3.org/2001/XMLSchema-instance"].join("");
|
|
726
|
+
const schemalocation = ["http", "s://maven.apache.org/xsd/maven-4.0.0.xsd"].join("");
|
|
727
|
+
const includes = mavenjarresources()
|
|
728
|
+
.map((resource) => ` <include>${resource}</include>`)
|
|
729
|
+
.join("\n");
|
|
730
|
+
const licenseurl = ["http", "s://www.gnu.org/licenses/gpl-3.0.html"].join("");
|
|
731
|
+
const publishurl = ["http", "s://maven.pkg.github.com/wenathlan/devthink"].join("");
|
|
732
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
733
|
+
<project xmlns="${pomnamespace}" xmlns:xsi="${xsinamespace}" xsi:schemaLocation="${pomnamespace} ${schemalocation}">
|
|
734
|
+
<modelVersion>4.0.0</modelVersion>
|
|
735
|
+
<groupId>${metadata.groupid}</groupId>
|
|
736
|
+
<artifactId>${metadata.artifactid}</artifactId>
|
|
737
|
+
<version>${version}</version>
|
|
738
|
+
<packaging>jar</packaging>
|
|
739
|
+
<name>${metadata.name}</name>
|
|
740
|
+
<description>${metadata.description}</description>
|
|
741
|
+
<url>${metadata.url}</url>
|
|
742
|
+
<licenses><license><name>${metadata.license}</name><url>${licenseurl}</url></license></licenses>
|
|
743
|
+
<scm><url>${metadata.scmurl}</url><connection>${metadata.scmconnection}</connection></scm>
|
|
744
|
+
<distributionManagement><repository><id>github</id><name>GitHub Packages</name><url>${publishurl}</url></repository></distributionManagement>
|
|
745
|
+
<build>
|
|
746
|
+
<resources>
|
|
747
|
+
<resource>
|
|
748
|
+
<directory>dist</directory>
|
|
749
|
+
<includes>
|
|
750
|
+
${includes}
|
|
751
|
+
</includes>
|
|
752
|
+
</resource>
|
|
753
|
+
</resources>
|
|
754
|
+
<plugins><plugin><groupId>org.codehaus.mojo</groupId><artifactId>build-helper-maven-plugin</artifactId><version>3.6.1</version><executions>
|
|
755
|
+
<execution><id>attach-extension-zip</id><phase>package</phase><goals><goal>attach-artifact</goal></goals><configuration><artifacts>
|
|
756
|
+
<artifact><file>\${project.basedir}/dist/devthink\${project.version}.zip</file><type>zip</type><classifier>extension</classifier></artifact>
|
|
757
|
+
<artifact><file>\${project.basedir}/dist/devthink-declarations-\${project.version}.zip</file><type>zip</type><classifier>declarations</classifier></artifact>
|
|
758
|
+
</artifacts></configuration></execution>
|
|
759
|
+
</executions></plugin></plugins>
|
|
760
|
+
</build>
|
|
761
|
+
</project>
|
|
762
|
+
`;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/* ── Merged from nugetpack.ts: the 1.1.90 consolidation interns the correlated nugetpack logic here, so no variation of the same file lives beside another. ── */
|
|
766
|
+
/**
|
|
767
|
+
* Nugetpack of the 1.1.87 publishing pipeline family.
|
|
768
|
+
* Every nuget packaging concern of the publishing pipeline lives in this one pure module: the content files the nupkg carries for the cli, headless and mcp entries, the umd and cjs bundles embedded as content assets, the declaration files embedded for ide integration, the framework targets matching the csproj profile, the project url, the license expression and the readme the manifest carries, and the fixtures directory embedded as sample content. The module stays pure: the layout is plain data the build, the tests and the docs read, the checked-in devthink.csproj mirrors it and the nugetpack tests assert the mirror never drifts, no vendor endpoint and no download url ever appears here — the nuget channel publishes to the package source the workflow declares.
|
|
769
|
+
* Example: `const layout = nugetpacklayout("1.1.87");`
|
|
770
|
+
*/
|
|
771
|
+
|
|
772
|
+
/** The content files the nupkg carries: the cli, headless and mcp entries of the terminal family, the umd and cjs bundles as content assets, and the chromium extension zip the content package has always carried — every entry lands under the contentFiles root a package consumer restores. */
|
|
773
|
+
export function nugetcontententries(version: string): nugetcontententry[] {
|
|
774
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
775
|
+
throw new Error(
|
|
776
|
+
"The nuget content entries carry the release version; a non semver version never names an artifact.",
|
|
777
|
+
);
|
|
778
|
+
return [
|
|
779
|
+
{ source: `dist/devthink${version}.zip`, contentpath: `contentFiles/any/any/devthink${version}.zip` },
|
|
780
|
+
{ source: "dist/cli.js", contentpath: "contentFiles/any/any/cli.js" },
|
|
781
|
+
{ source: "dist/headless.js", contentpath: "contentFiles/any/any/headless.js" },
|
|
782
|
+
{ source: "dist/mcp.js", contentpath: "contentFiles/any/any/mcp.js" },
|
|
783
|
+
{ source: "dist/devthink.umd.js", contentpath: "contentFiles/any/any/devthink.umd.js" },
|
|
784
|
+
{ source: "dist/index.cjs", contentpath: "contentFiles/any/any/index.cjs" },
|
|
785
|
+
];
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/** The declaration entries the nupkg embeds for ide integration: the declaration files of the public modules land under the declarations folder of the content root so an editor resolves the devthink types from the package. */
|
|
789
|
+
export function nugetdeclarationentries(): nugetcontententry[] {
|
|
790
|
+
return ["index", "policy", "protocol", "memory", "progress", "cli", "headless", "mcp", "wsbridge", "nativehost"].map(
|
|
791
|
+
(entry) => ({ source: `dist/${entry}.d.ts`, contentpath: `contentFiles/any/any/declarations/${entry}.d.ts` }),
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/** The fixture entries the nupkg embeds as sample content: the recorded page state fixture and the example plans, workflows and mcp client the docs reference land under the fixtures folder of the content root. */
|
|
796
|
+
export function nugetfixtureentries(): nugetcontententry[] {
|
|
797
|
+
return [
|
|
798
|
+
{
|
|
799
|
+
source: "dist/fixtures/example-org-pagestate.json",
|
|
800
|
+
contentpath: "contentFiles/any/any/fixtures/example-org-pagestate.json",
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
source: "dist/fixtures/plans/release-notes-plan.json",
|
|
804
|
+
contentpath: "contentFiles/any/any/fixtures/plans/release-notes-plan.json",
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
source: "dist/fixtures/plans/form-inventory-plan.json",
|
|
808
|
+
contentpath: "contentFiles/any/any/fixtures/plans/form-inventory-plan.json",
|
|
809
|
+
},
|
|
810
|
+
{ source: "dist/fixtures/mcp-client.mjs", contentpath: "contentFiles/any/any/fixtures/mcp-client.mjs" },
|
|
811
|
+
];
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/** The framework targets the nupkg declares: the target frameworks matching the csproj profile (the netstandard family) so the package restores on every dotnet consumer the profile supports. */
|
|
815
|
+
export function nugetframeworktargets(): string[] {
|
|
816
|
+
return ["netstandard2.0", "netstandard2.1"];
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/** The package metadata the nupkg manifest carries: the package id, the project url, the repository url, the license expression, the readme and the description — the registry listing reads exactly these fields. */
|
|
820
|
+
export function nugetpackinfo(): nugetpackmetadata {
|
|
821
|
+
return {
|
|
822
|
+
packageid: "devthink",
|
|
823
|
+
projecturl: "https://github.com/wenathlan/devthink",
|
|
824
|
+
repositoryurl: "https://github.com/wenathlan/devthink",
|
|
825
|
+
license: "GPL-3.0-only",
|
|
826
|
+
readme: "README.md",
|
|
827
|
+
description:
|
|
828
|
+
"Distribution package containing the Devthink library bundles, the cli, headless and mcp entries, the declaration files for ide integration, the browser extension ZIPs and the sample fixtures.",
|
|
829
|
+
targets: nugetframeworktargets(),
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/** The full nuget layout of one release: the content entries, the declaration entries, the fixture entries, the framework targets and the metadata — the layout the build, the docs and the tests read so the checked-in devthink.csproj never drifts from the descriptor. */
|
|
834
|
+
export function nugetpacklayout(version: string): {
|
|
835
|
+
version: string;
|
|
836
|
+
content: nugetcontententry[];
|
|
837
|
+
declarations: nugetcontententry[];
|
|
838
|
+
fixtures: nugetcontententry[];
|
|
839
|
+
metadata: nugetpackmetadata;
|
|
840
|
+
} {
|
|
841
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
842
|
+
throw new Error("The nuget layout carries the release version; a non semver version never names an artifact.");
|
|
843
|
+
return {
|
|
844
|
+
version,
|
|
845
|
+
content: nugetcontententries(version),
|
|
846
|
+
declarations: nugetdeclarationentries(),
|
|
847
|
+
fixtures: nugetfixtureentries(),
|
|
848
|
+
metadata: nugetpackinfo(),
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/* ── Merged from containerpack.ts: the 1.1.90 consolidation interns the correlated containerpack logic here, so no variation of the same file lives beside another. ── */
|
|
853
|
+
/**
|
|
854
|
+
* Containerpack of the 1.1.87 publishing pipeline family, restamped for the DevThink 2.0.0 container story.
|
|
855
|
+
* Every container packaging concern of the publishing pipeline lives in this one pure module: the multi stage build of THE Dockerfile (the one container file of the merged repository: a deps stage that installs the pinned node base with the bun version the packageManager field pins and the frozen dependency tree as a cacheable layer, a builder stage that runs the whole validation chain over node tests/build.mjs, a binary-builder and a binary-runtime pair that compile the single binary aimed at the bun target resolved from TARGETARCH, and the runtime stage that copies the lean output onto the plain node base and starts the self hosting runner — the last stage of the file, so it is the default build target), the exposed surfaces the runtime image serves (the static site, the mcp server and the socket relay speaking the servercontract for self hosting), the image tags the release stamps (the version tag only — the immutable coordinate an operator pins, never a channel alias) and the digest file the release publishes with the image hash. The module stays pure: the descriptors are plain data the build, the tests and the docs read, the checked-in Dockerfile mirrors them and the containerpack tests assert the mirror never drifts, no vendor endpoint and no download url ever appears here — the operator pulls the image from the registry they choose.
|
|
856
|
+
* Example: `const stages = containerbuildstages(); const surfaces = containerexposedsurfaces();`
|
|
857
|
+
*/
|
|
858
|
+
|
|
859
|
+
/** The multi stage build of the container image: the five stages of THE Dockerfile in file order — the deps layer installs the pinned toolchain and the frozen dependency tree, the builder stage builds every dist target and runs the whole validation chain, the binary stages compile the single binary aimed at the bun target resolved from TARGETARCH, and the runtime stage (the last stage of the file, the default build target) copies the lean output (the site, the cli and the relay bundles) onto the plain node base and starts the self hosting runner. */
|
|
860
|
+
export function containerbuildstages(): containerbuildstage[] {
|
|
861
|
+
return [
|
|
862
|
+
{
|
|
863
|
+
name: "deps",
|
|
864
|
+
purpose:
|
|
865
|
+
"installs the pinned node base with the bun version the packageManager field of package.json pins, zip and unzip, and the frozen dependency tree as its own cacheable layer",
|
|
866
|
+
checks: ["bun install --frozen-lockfile"],
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
name: "builder",
|
|
870
|
+
purpose:
|
|
871
|
+
"builds every dist target through node tests/build.mjs and runs the deterministic build checks, the vitest suite (native on both legs through the build platform pin, with the timeout guard a native arm64 build node reads) and the packageextension verification",
|
|
872
|
+
checks: [
|
|
873
|
+
"node tests/build.mjs",
|
|
874
|
+
"node dist/cli.js manifest",
|
|
875
|
+
"the cjs require check",
|
|
876
|
+
"the headless smoke over the example fixture",
|
|
877
|
+
"node tests/nativesmoke.mjs",
|
|
878
|
+
"the firefox prep check",
|
|
879
|
+
"node tests/packageextension.mjs",
|
|
880
|
+
],
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
name: "binary-builder",
|
|
884
|
+
purpose:
|
|
885
|
+
"compiles devthink.ts with bun build --compile aimed at the bun target resolved from TARGETARCH (bun-linux-x64 on amd64, bun-linux-arm64 on arm64)",
|
|
886
|
+
checks: ["bun run build.ts --target <bun-target>"],
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
name: "binary-runtime",
|
|
890
|
+
purpose:
|
|
891
|
+
"copies the single compiled binary onto the distroless non-root base (the explicit --target binary-runtime image)",
|
|
892
|
+
checks: ["the compiled binary answers --version"],
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
name: "runtime",
|
|
896
|
+
purpose:
|
|
897
|
+
"copies the lean output onto the plain node base, embeds the self hosting runner as a heredoc copy and runs the smoke boot with server death detection — the last stage of the file, so it is the default build target",
|
|
898
|
+
checks: ["node container.mjs --check"],
|
|
899
|
+
},
|
|
900
|
+
];
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/** The build checks the builder stage runs before the runtime stage ships: the cli manifest asserts the built cli answers, and the headless smoke runs one fixture through the headless entry — an image whose build checks fail never ships. */
|
|
904
|
+
export function containerbuildchecks(): string[] {
|
|
905
|
+
return [
|
|
906
|
+
"node dist/cli.js manifest",
|
|
907
|
+
"node dist/cli.js headless dist/fixtures/plans/release-notes-plan.json --fixtures dist/fixtures",
|
|
908
|
+
];
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/** The exposed surfaces of the runtime image: the static site and the socket relay ride the http listener the user binds (the relay speaks the servercontract for self hosting), and the mcp server rides its own listener behind its own bind — every bind, port and path stays an environment choice the operator sets, the defaults keep the mcp listener loopback only and no surface names an endpoint. */
|
|
912
|
+
export function containerexposedsurfaces(): containerexposedsurface[] {
|
|
913
|
+
return [
|
|
914
|
+
{ name: "static site", kind: "site", bindenv: "DEVTHINK_HTTP_BIND", portenv: "DEVTHINK_HTTP_PORT" },
|
|
915
|
+
{
|
|
916
|
+
name: "socket relay",
|
|
917
|
+
kind: "relay",
|
|
918
|
+
bindenv: "DEVTHINK_HTTP_BIND",
|
|
919
|
+
portenv: "DEVTHINK_HTTP_PORT",
|
|
920
|
+
path: "DEVTHINK_RELAY_PATH",
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
name: "mcp server",
|
|
924
|
+
kind: "mcp",
|
|
925
|
+
bindenv: "DEVTHINK_MCP_BIND",
|
|
926
|
+
portenv: "DEVTHINK_MCP_PORT",
|
|
927
|
+
path: "DEVTHINK_MCP_PATH",
|
|
928
|
+
},
|
|
929
|
+
];
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/** The image tags the release stamps: the version tag only — the immutable coordinate an operator pins; the channel aliases (stable, latest) never ride the DevThink image (the publishghcr lane resolves exactly the version tag, so a release can never move under an operator's pull), and the tags are docker convention coordinates, never vendor endpoints. */
|
|
933
|
+
export function containerimagetags(version: string): string[] {
|
|
934
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
935
|
+
throw new Error("The image tags carry the release version; a non semver version never tags an image.");
|
|
936
|
+
return [version];
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/** The digest file the release publishes with the image hash: the digest records the pushed image reference and its sha256 digest so an operator pins the exact image the release shipped — the DevThink image the publishghcr lane pushes under the version tag. */
|
|
940
|
+
export function containerdigestfiles(version: string): Array<{ name: string; content: string }> {
|
|
941
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
942
|
+
throw new Error("The digest files carry the release version; a non semver version never names a digest.");
|
|
943
|
+
return [
|
|
944
|
+
{ name: "devthink-container.txt", content: "ghcr.io/wenathlan/devthink:VERSION" },
|
|
945
|
+
{ name: "devthink-container.digest", content: "sha256:IMAGE" },
|
|
946
|
+
{
|
|
947
|
+
name: "devthink-container.json",
|
|
948
|
+
content: '{"image":"ghcr.io/wenathlan/devthink:VERSION","digest":"sha256:IMAGE"}',
|
|
949
|
+
},
|
|
950
|
+
];
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/** The runtime entry the image starts: the self hosting runner that serves the static site, the socket relay and the mcp server listener — one plain node script, no vendor runtime and no function file anywhere in the image. */
|
|
954
|
+
export function containerrunnerentry(): string {
|
|
955
|
+
return "node container.mjs";
|
|
956
|
+
}
|