tines 0.0.149 → 0.0.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +100 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3775,6 +3775,46 @@ function requirementFix(r, ref) {
|
|
|
3775
3775
|
};
|
|
3776
3776
|
}
|
|
3777
3777
|
|
|
3778
|
+
// ../shared/src/artifact-site.ts
|
|
3779
|
+
var ARTIFACT_SITE_LINK_TTL_MS = 60 * 60 * 1e3;
|
|
3780
|
+
var ARTIFACT_SITE_INDEX = "index.html";
|
|
3781
|
+
function siteEntry(type, contentType, files = []) {
|
|
3782
|
+
if (type === "file" || type === "text") {
|
|
3783
|
+
return (contentType ?? "").toLowerCase().startsWith("text/html") ? "" : null;
|
|
3784
|
+
}
|
|
3785
|
+
if (type === "folder") {
|
|
3786
|
+
return files.some((f) => f.path === ARTIFACT_SITE_INDEX) ? ARTIFACT_SITE_INDEX : null;
|
|
3787
|
+
}
|
|
3788
|
+
return null;
|
|
3789
|
+
}
|
|
3790
|
+
var VIEWPORT_META = /<meta[^>]+name\s*=\s*["']?viewport["']?[^>]*content\s*=\s*["'][^"']*width\s*=\s*device-width/i;
|
|
3791
|
+
var EXTERNAL_URL = /^(?:https?:)?\/\//i;
|
|
3792
|
+
function externalRefs(html) {
|
|
3793
|
+
const found = [];
|
|
3794
|
+
const push = (url) => {
|
|
3795
|
+
if (url && EXTERNAL_URL.test(url.trim()) && !found.includes(url.trim())) found.push(url.trim());
|
|
3796
|
+
};
|
|
3797
|
+
for (const m of html.matchAll(/<script[^>]*\ssrc\s*=\s*["']([^"']+)["']/gi)) push(m[1]);
|
|
3798
|
+
for (const m of html.matchAll(/<link[^>]*\shref\s*=\s*["']([^"']+)["']/gi)) push(m[1]);
|
|
3799
|
+
for (const m of html.matchAll(/@import\s+(?:url\()?\s*["']([^"']+)["']/gi)) push(m[1]);
|
|
3800
|
+
return found;
|
|
3801
|
+
}
|
|
3802
|
+
function lintHtmlArtifact(html) {
|
|
3803
|
+
const warnings = [];
|
|
3804
|
+
if (!VIEWPORT_META.test(html)) {
|
|
3805
|
+
warnings.push(
|
|
3806
|
+
'no viewport meta tag \u2014 add <meta name="viewport" content="width=device-width, initial-scale=1"> so it lays out for phones'
|
|
3807
|
+
);
|
|
3808
|
+
}
|
|
3809
|
+
const external = externalRefs(html);
|
|
3810
|
+
if (external.length > 0) {
|
|
3811
|
+
warnings.push(
|
|
3812
|
+
`external scripts/styles are blocked by the sandbox policy (${external.slice(0, 3).join(", ")}${external.length > 3 ? ", \u2026" : ""}) \u2014 inline or vendor them into the artifact`
|
|
3813
|
+
);
|
|
3814
|
+
}
|
|
3815
|
+
return warnings;
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3778
3818
|
// ../shared/src/client.ts
|
|
3779
3819
|
var ApiError = class extends Error {
|
|
3780
3820
|
status;
|
|
@@ -3957,6 +3997,11 @@ function createApiClient(options) {
|
|
|
3957
3997
|
// Issue artifacts (name-addressed under the issue)
|
|
3958
3998
|
listArtifacts: (issueId) => get(`/api/v1/issues/${issueId}/artifacts`),
|
|
3959
3999
|
getArtifact: (issueId, name2) => get(artifactPath(issueId, name2)),
|
|
4000
|
+
/**
|
|
4001
|
+
* Mints a short-lived signed URL that renders an HTML artifact live
|
|
4002
|
+
* (422 `not_a_site` when the artifact is not HTML / has no index.html).
|
|
4003
|
+
*/
|
|
4004
|
+
createArtifactSiteLink: (issueId, name2, body = {}) => request("POST", artifactPath(issueId, name2, "/site-link"), body),
|
|
3960
4005
|
/** JSON upsert for text/link/pr: creates the artifact or appends a version. */
|
|
3961
4006
|
putArtifact: (issueId, name2, body) => request("PUT", artifactPath(issueId, name2), body),
|
|
3962
4007
|
/** Raw-body upload for `file`: creates the artifact or appends a version. */
|
|
@@ -5887,6 +5932,16 @@ skills: ${context.skills.map((s) => s.name).join(", ")}`);
|
|
|
5887
5932
|
`current: v${artifact.current_version.version} (${artifact.fresh ? "fresh" : "attached before the current state \u2014 reaffirm or attach a new version to satisfy gates"})`
|
|
5888
5933
|
);
|
|
5889
5934
|
console.log(`summary: ${artifactSummary(artifact)}`);
|
|
5935
|
+
const entry = siteEntry(
|
|
5936
|
+
artifact.artifact_type,
|
|
5937
|
+
artifact.current_version.content_type,
|
|
5938
|
+
artifact.current_version.files ?? []
|
|
5939
|
+
);
|
|
5940
|
+
if (entry !== null) {
|
|
5941
|
+
console.log(
|
|
5942
|
+
`site: renders live from /${entry} \u2014 "tines issues artifacts site-link ${issue.project_name}/${issue.number} ${artifact.name}" for a viewable URL`
|
|
5943
|
+
);
|
|
5944
|
+
}
|
|
5890
5945
|
console.log("\nversions:");
|
|
5891
5946
|
table(
|
|
5892
5947
|
artifact.versions.map((v) => [
|
|
@@ -5936,10 +5991,13 @@ files (v${artifact.current_version.version}):`);
|
|
|
5936
5991
|
);
|
|
5937
5992
|
const withDescription = opts.description !== void 0 ? { description: opts.description } : {};
|
|
5938
5993
|
let artifact;
|
|
5994
|
+
let siteHtml = null;
|
|
5939
5995
|
if (plan.type === "folder") {
|
|
5940
5996
|
const dir = plan.source.dir;
|
|
5941
5997
|
const files = walkFolder(dir);
|
|
5942
5998
|
if (files.length === 0) die(`${dir} contains no files to snapshot`);
|
|
5999
|
+
const index = files.find((f) => f.path === ARTIFACT_SITE_INDEX);
|
|
6000
|
+
if (index) siteHtml = index.bytes.toString("utf8");
|
|
5943
6001
|
artifact = await api.uploadArtifactFolder(issue.id, name2, files);
|
|
5944
6002
|
if (opts.description !== void 0) {
|
|
5945
6003
|
artifact = await api.putArtifact(issue.id, name2, { description: opts.description });
|
|
@@ -5957,15 +6015,18 @@ files (v${artifact.current_version.version}):`);
|
|
|
5957
6015
|
die(`cannot read ${fromPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5958
6016
|
}
|
|
5959
6017
|
}
|
|
6018
|
+
const contentType = plan.contentType ?? (fromPath === null ? "application/octet-stream" : sniffContentType(fromPath));
|
|
6019
|
+
if (siteEntry("file", contentType) !== null) siteHtml = bytes.toString("utf8");
|
|
5960
6020
|
artifact = await api.uploadArtifactFile(issue.id, name2, bytes, {
|
|
5961
6021
|
filename: plan.filename ?? (fromPath === null ? name2 : basename(fromPath)),
|
|
5962
|
-
contentType
|
|
6022
|
+
contentType
|
|
5963
6023
|
});
|
|
5964
6024
|
if (opts.description !== void 0) {
|
|
5965
6025
|
artifact = await api.putArtifact(issue.id, name2, { description: opts.description });
|
|
5966
6026
|
}
|
|
5967
6027
|
} else if (plan.type === "text") {
|
|
5968
6028
|
const content = readTextSource(plan.source);
|
|
6029
|
+
if (siteEntry("text", plan.contentType ?? null) !== null) siteHtml = content;
|
|
5969
6030
|
artifact = await api.putArtifact(issue.id, name2, {
|
|
5970
6031
|
type: "text",
|
|
5971
6032
|
content,
|
|
@@ -5996,6 +6057,12 @@ files (v${artifact.current_version.version}):`);
|
|
|
5996
6057
|
console.log(
|
|
5997
6058
|
`attached "${artifact.name}" v${artifact.current_version.version} (${artifactTypeLabel(artifact)}) to ${issue.project_name}/${issue.number} \u2014 fresh${gateNote}`
|
|
5998
6059
|
);
|
|
6060
|
+
if (siteHtml !== null) {
|
|
6061
|
+
console.log(
|
|
6062
|
+
`site: renders live \u2014 "tines issues artifacts site-link ${issue.project_name}/${issue.number} ${artifact.name}" for a viewable URL`
|
|
6063
|
+
);
|
|
6064
|
+
for (const warning of lintHtmlArtifact(siteHtml)) console.log(`warning: ${warning}`);
|
|
6065
|
+
}
|
|
5999
6066
|
}
|
|
6000
6067
|
);
|
|
6001
6068
|
withCommon(
|
|
@@ -6011,6 +6078,32 @@ files (v${artifact.current_version.version}):`);
|
|
|
6011
6078
|
`reaffirmed "${artifact.name}" on ${issue.project_name}/${issue.number}: v${artifact.current_version.version} reaffirms v${artifact.current_version.reaffirmed_from} \u2014 fresh as of now`
|
|
6012
6079
|
);
|
|
6013
6080
|
});
|
|
6081
|
+
withCommon(
|
|
6082
|
+
artifactsCmd.command("site-link <ref> <name>").description(
|
|
6083
|
+
"Mint a short-lived URL that renders an HTML artifact live (scripts running) \u2014 for screenshotting your own prototype"
|
|
6084
|
+
).option(
|
|
6085
|
+
"--version <n>",
|
|
6086
|
+
"pin the link to a specific version (defaults to current)",
|
|
6087
|
+
(v) => Number.parseInt(v, 10)
|
|
6088
|
+
)
|
|
6089
|
+
).action(async (ref, name2, opts) => {
|
|
6090
|
+
const api = client(opts);
|
|
6091
|
+
const issue = await resolveIssue(api, ref);
|
|
6092
|
+
const link = await api.createArtifactSiteLink(
|
|
6093
|
+
issue.id,
|
|
6094
|
+
name2,
|
|
6095
|
+
opts.version === void 0 ? {} : { version: opts.version }
|
|
6096
|
+
);
|
|
6097
|
+
if (opts.json) return printJson(link);
|
|
6098
|
+
console.log(link.url);
|
|
6099
|
+
const minutes = Math.max(1, Math.round((link.expires_at - Date.now()) / 6e4));
|
|
6100
|
+
console.log(`v${link.version}, expires in ~${minutes} minute${minutes === 1 ? "" : "s"}`);
|
|
6101
|
+
if (link.mode === "same-origin") {
|
|
6102
|
+
console.log(
|
|
6103
|
+
"mode: same-origin sandbox (opaque origin) \u2014 localStorage and cookies throw inside the page"
|
|
6104
|
+
);
|
|
6105
|
+
}
|
|
6106
|
+
});
|
|
6014
6107
|
withCommon(
|
|
6015
6108
|
artifactsCmd.command("get <ref> <name>").description("Fetch content (current version by default); a link/pr prints its URL").option(
|
|
6016
6109
|
"--version <n>",
|
|
@@ -6990,7 +7083,12 @@ function buildHarnessInvocation(spec, input) {
|
|
|
6990
7083
|
case "codex":
|
|
6991
7084
|
return {
|
|
6992
7085
|
file: "codex",
|
|
6993
|
-
args: [
|
|
7086
|
+
args: [
|
|
7087
|
+
"exec",
|
|
7088
|
+
"--skip-git-repo-check",
|
|
7089
|
+
...input.model ? ["--model", input.model] : [],
|
|
7090
|
+
input.prompt
|
|
7091
|
+
]
|
|
6994
7092
|
};
|
|
6995
7093
|
case "custom": {
|
|
6996
7094
|
if (!spec.command) throw new Error("the custom harness needs a --command template");
|