@hasna/todos 0.15.10 → 0.15.12
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/cli/cloud-router.d.ts +10 -1
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +723 -89
- package/dist/contracts.js +1 -1
- package/dist/index.js +2 -1
- package/dist/lib/plan-project-link-contract.d.ts +16 -1
- package/dist/lib/plan-project-link-contract.d.ts.map +1 -1
- package/dist/mcp/index.js +232 -82
- package/dist/mcp.js +1 -1
- package/dist/project-registration.js +2 -1
- package/dist/registry.js +1 -1
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/index.js +21 -0
- package/dist/sdk/v1.generated.d.ts +32 -0
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/index.js +156 -6
- package/dist/server/openapi.d.ts +183 -0
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/storage.js +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2123,7 +2123,7 @@ var package_default;
|
|
|
2123
2123
|
var init_package = __esm(() => {
|
|
2124
2124
|
package_default = {
|
|
2125
2125
|
name: "@hasna/todos",
|
|
2126
|
-
version: "0.15.
|
|
2126
|
+
version: "0.15.12",
|
|
2127
2127
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
2128
2128
|
type: "module",
|
|
2129
2129
|
main: "dist/index.js",
|
|
@@ -2269,7 +2269,7 @@ function isBlockingDependencyStatus(status) {
|
|
|
2269
2269
|
function isTerminalStatus(status) {
|
|
2270
2270
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
2271
2271
|
}
|
|
2272
|
-
var TASK_STATUSES, TASK_PRIORITIES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
|
|
2272
|
+
var TASK_STATUSES, TASK_PRIORITIES, PLAN_STATUSES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
|
|
2273
2273
|
var init_types = __esm(() => {
|
|
2274
2274
|
TASK_STATUSES = [
|
|
2275
2275
|
"pending",
|
|
@@ -2284,6 +2284,7 @@ var init_types = __esm(() => {
|
|
|
2284
2284
|
"high",
|
|
2285
2285
|
"critical"
|
|
2286
2286
|
];
|
|
2287
|
+
PLAN_STATUSES = ["active", "completed", "archived"];
|
|
2287
2288
|
VersionConflictError = class VersionConflictError extends Error {
|
|
2288
2289
|
taskId;
|
|
2289
2290
|
expectedVersion;
|
|
@@ -2822,6 +2823,405 @@ var init_redaction = __esm(() => {
|
|
|
2822
2823
|
REDACTION_PLACEHOLDER = String.raw`\[REDACTED(?:_[A-Z_]+)?\]`;
|
|
2823
2824
|
});
|
|
2824
2825
|
|
|
2826
|
+
// src/lib/plan-project-link-contract.ts
|
|
2827
|
+
import { createHash as createHash2 } from "crypto";
|
|
2828
|
+
function canonicalPlanProjectLinkJson(value) {
|
|
2829
|
+
if (value === null || typeof value !== "object")
|
|
2830
|
+
return JSON.stringify(value);
|
|
2831
|
+
if (Array.isArray(value))
|
|
2832
|
+
return `[${value.map(canonicalPlanProjectLinkJson).join(",")}]`;
|
|
2833
|
+
return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${canonicalPlanProjectLinkJson(item)}`).join(",")}}`;
|
|
2834
|
+
}
|
|
2835
|
+
function planProjectLinkDigest(value) {
|
|
2836
|
+
return createHash2("sha256").update(canonicalPlanProjectLinkJson(value)).digest("hex");
|
|
2837
|
+
}
|
|
2838
|
+
function normalizePlanProjectLinkIdempotencyKey(value) {
|
|
2839
|
+
const key = value?.trim() ?? "";
|
|
2840
|
+
if (key.length < 8 || key.length > 128 || !/^[A-Za-z0-9._:-]+$/.test(key)) {
|
|
2841
|
+
throw new PlanProjectLinkError("PLAN_PROJECT_LINK_IDEMPOTENCY_KEY_INVALID", "idempotency_key must be 8-128 ASCII letters, digits, dots, underscores, colons, or hyphens");
|
|
2842
|
+
}
|
|
2843
|
+
return key;
|
|
2844
|
+
}
|
|
2845
|
+
function planProjectLinkReceiptId(idempotencyKey) {
|
|
2846
|
+
return `pplr_${planProjectLinkDigest({ idempotency_key: idempotencyKey }).slice(0, 48)}`;
|
|
2847
|
+
}
|
|
2848
|
+
function planProjectLinkRollbackReceiptId(receiptId) {
|
|
2849
|
+
return `pplr_inverse_${planProjectLinkDigest({ accepted_receipt_id: receiptId }).slice(0, 38)}`;
|
|
2850
|
+
}
|
|
2851
|
+
function planProjectLinkRequestHash(planId, projectId) {
|
|
2852
|
+
return planProjectLinkDigest({ plan_id: planId, project_id: projectId });
|
|
2853
|
+
}
|
|
2854
|
+
function planProjectLinkResultDigest(plan, tasks) {
|
|
2855
|
+
return planProjectLinkDigest({
|
|
2856
|
+
plan_id: plan.id,
|
|
2857
|
+
plan_project_id: plan.project_id,
|
|
2858
|
+
tasks: tasks.map((task) => ({ id: task.id, plan_id: task.plan_id, project_id: task.project_id })).sort((left, right) => left.id.localeCompare(right.id))
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
function assertPlanProjectLinkReceipt(value) {
|
|
2862
|
+
const receipt = value;
|
|
2863
|
+
if (!receipt || typeof receipt !== "object" || receipt.schema_version !== PLAN_PROJECT_LINK_SCHEMA_VERSION || typeof receipt.receipt_id !== "string" || typeof receipt.idempotency_key !== "string" || typeof receipt.plan_id !== "string" || typeof receipt.project_id !== "string" || !Array.isArray(receipt.task_ids) || receipt.task_ids.some((id) => typeof id !== "string") || !receipt.prior_task_project_ids || typeof receipt.prior_task_project_ids !== "object" || typeof receipt.result_digest !== "string") {
|
|
2864
|
+
throw new PlanProjectLinkError("PLAN_PROJECT_LINK_RECEIPT_NOT_FOUND", "Stored plan-project-link receipt is invalid");
|
|
2865
|
+
}
|
|
2866
|
+
return receipt;
|
|
2867
|
+
}
|
|
2868
|
+
function responseRecord(value, label) {
|
|
2869
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2870
|
+
throw new Error(`${label} must be an object`);
|
|
2871
|
+
}
|
|
2872
|
+
return value;
|
|
2873
|
+
}
|
|
2874
|
+
function exactResponseKeys(value, expected, label) {
|
|
2875
|
+
const actual = Object.keys(value).sort();
|
|
2876
|
+
const wanted = [...expected].sort();
|
|
2877
|
+
if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
|
|
2878
|
+
throw new Error(`${label} fields must be exactly: ${wanted.join(", ")}`);
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
function responseString(value, label) {
|
|
2882
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2883
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
2884
|
+
}
|
|
2885
|
+
return value;
|
|
2886
|
+
}
|
|
2887
|
+
function responseNullableString(value, label) {
|
|
2888
|
+
if (value !== null && typeof value !== "string") {
|
|
2889
|
+
throw new Error(`${label} must be a string or null`);
|
|
2890
|
+
}
|
|
2891
|
+
return value;
|
|
2892
|
+
}
|
|
2893
|
+
function responseNumber(value, label) {
|
|
2894
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2895
|
+
throw new Error(`${label} must be a finite number`);
|
|
2896
|
+
}
|
|
2897
|
+
return value;
|
|
2898
|
+
}
|
|
2899
|
+
function responseBoolean(value, label) {
|
|
2900
|
+
if (typeof value !== "boolean")
|
|
2901
|
+
throw new Error(`${label} must be a boolean`);
|
|
2902
|
+
return value;
|
|
2903
|
+
}
|
|
2904
|
+
function responseDateTime(value, label) {
|
|
2905
|
+
const timestamp = responseString(value, label);
|
|
2906
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(timestamp) || !Number.isFinite(Date.parse(timestamp))) {
|
|
2907
|
+
throw new Error(`${label} must be an RFC 3339 date-time`);
|
|
2908
|
+
}
|
|
2909
|
+
return timestamp;
|
|
2910
|
+
}
|
|
2911
|
+
function responseNullableDateTime(value, label) {
|
|
2912
|
+
if (value === null)
|
|
2913
|
+
return null;
|
|
2914
|
+
return responseDateTime(value, label);
|
|
2915
|
+
}
|
|
2916
|
+
function responseOptionalNullableString(record, field, label) {
|
|
2917
|
+
if (field in record)
|
|
2918
|
+
responseNullableString(record[field], `${label}.${field}`);
|
|
2919
|
+
}
|
|
2920
|
+
function responseOptionalNullableDateTime(record, field, label) {
|
|
2921
|
+
if (field in record)
|
|
2922
|
+
responseNullableDateTime(record[field], `${label}.${field}`);
|
|
2923
|
+
}
|
|
2924
|
+
function responseStringArray(value, label) {
|
|
2925
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
2926
|
+
throw new Error(`${label} must be an array of strings`);
|
|
2927
|
+
}
|
|
2928
|
+
return value;
|
|
2929
|
+
}
|
|
2930
|
+
function responseProjectSources(value, expectedProjectId) {
|
|
2931
|
+
if (!Array.isArray(value))
|
|
2932
|
+
throw new Error("project.sources must be an array");
|
|
2933
|
+
value.forEach((item, index) => {
|
|
2934
|
+
const label = `project.sources[${index}]`;
|
|
2935
|
+
const source = responseRecord(item, label);
|
|
2936
|
+
responseString(source.id, `${label}.id`);
|
|
2937
|
+
if (responseString(source.project_id, `${label}.project_id`) !== expectedProjectId) {
|
|
2938
|
+
throw new Error(`${label}.project_id must match project.id`);
|
|
2939
|
+
}
|
|
2940
|
+
responseString(source.type, `${label}.type`);
|
|
2941
|
+
responseString(source.name, `${label}.name`);
|
|
2942
|
+
responseString(source.uri, `${label}.uri`);
|
|
2943
|
+
responseNullableString(source.description, `${label}.description`);
|
|
2944
|
+
responseRecord(source.metadata, `${label}.metadata`);
|
|
2945
|
+
responseDateTime(source.created_at, `${label}.created_at`);
|
|
2946
|
+
responseDateTime(source.updated_at, `${label}.updated_at`);
|
|
2947
|
+
});
|
|
2948
|
+
}
|
|
2949
|
+
function responsePlan(value, expectedPlanId) {
|
|
2950
|
+
const plan = responseRecord(value, "plan");
|
|
2951
|
+
if (responseString(plan.id, "plan.id") !== expectedPlanId) {
|
|
2952
|
+
throw new Error(`plan.id must match requested plan ${expectedPlanId}`);
|
|
2953
|
+
}
|
|
2954
|
+
responseNullableString(plan.slug, "plan.slug");
|
|
2955
|
+
responseNullableString(plan.project_id, "plan.project_id");
|
|
2956
|
+
responseNullableString(plan.task_list_id, "plan.task_list_id");
|
|
2957
|
+
responseNullableString(plan.agent_id, "plan.agent_id");
|
|
2958
|
+
responseString(plan.name, "plan.name");
|
|
2959
|
+
responseNullableString(plan.description, "plan.description");
|
|
2960
|
+
if (typeof plan.status !== "string" || !PLAN_STATUSES.includes(plan.status)) {
|
|
2961
|
+
throw new Error(`plan.status must be one of: ${PLAN_STATUSES.join(", ")}`);
|
|
2962
|
+
}
|
|
2963
|
+
responseDateTime(plan.created_at, "plan.created_at");
|
|
2964
|
+
responseDateTime(plan.updated_at, "plan.updated_at");
|
|
2965
|
+
responseOptionalNullableString(plan, "machine_id", "plan");
|
|
2966
|
+
responseOptionalNullableDateTime(plan, "synced_at", "plan");
|
|
2967
|
+
return plan;
|
|
2968
|
+
}
|
|
2969
|
+
function responseProject(value, expectedProjectId) {
|
|
2970
|
+
const project = responseRecord(value, "project");
|
|
2971
|
+
if (responseString(project.id, "project.id") !== expectedProjectId) {
|
|
2972
|
+
throw new Error(`project.id must match requested project ${expectedProjectId}`);
|
|
2973
|
+
}
|
|
2974
|
+
responseString(project.name, "project.name");
|
|
2975
|
+
responseString(project.path, "project.path");
|
|
2976
|
+
responseNullableString(project.description, "project.description");
|
|
2977
|
+
responseNullableString(project.task_list_id, "project.task_list_id");
|
|
2978
|
+
responseNullableString(project.task_prefix, "project.task_prefix");
|
|
2979
|
+
responseNumber(project.task_counter, "project.task_counter");
|
|
2980
|
+
responseDateTime(project.created_at, "project.created_at");
|
|
2981
|
+
responseDateTime(project.updated_at, "project.updated_at");
|
|
2982
|
+
responseOptionalNullableString(project, "machine_id", "project");
|
|
2983
|
+
responseOptionalNullableDateTime(project, "synced_at", "project");
|
|
2984
|
+
if ("sources" in project && project.sources !== undefined) {
|
|
2985
|
+
responseProjectSources(project.sources, expectedProjectId);
|
|
2986
|
+
}
|
|
2987
|
+
return project;
|
|
2988
|
+
}
|
|
2989
|
+
function responseTasks(value, expectedPlanId) {
|
|
2990
|
+
if (!Array.isArray(value))
|
|
2991
|
+
throw new Error("tasks must be an array");
|
|
2992
|
+
const seen = new Set;
|
|
2993
|
+
return value.map((item, index) => {
|
|
2994
|
+
const task = responseRecord(item, `tasks[${index}]`);
|
|
2995
|
+
const taskId = responseString(task.id, `tasks[${index}].id`);
|
|
2996
|
+
if (seen.has(taskId))
|
|
2997
|
+
throw new Error(`tasks contains duplicate id ${taskId}`);
|
|
2998
|
+
seen.add(taskId);
|
|
2999
|
+
if (task.plan_id !== expectedPlanId) {
|
|
3000
|
+
throw new Error(`tasks[${index}].plan_id must match requested plan ${expectedPlanId}`);
|
|
3001
|
+
}
|
|
3002
|
+
const label = `tasks[${index}]`;
|
|
3003
|
+
responseNullableString(task.short_id, `${label}.short_id`);
|
|
3004
|
+
responseNullableString(task.project_id, `tasks[${index}].project_id`);
|
|
3005
|
+
responseNullableString(task.parent_id, `${label}.parent_id`);
|
|
3006
|
+
responseNullableString(task.task_list_id, `${label}.task_list_id`);
|
|
3007
|
+
responseString(task.title, `${label}.title`);
|
|
3008
|
+
responseNullableString(task.description, `${label}.description`);
|
|
3009
|
+
if (typeof task.status !== "string" || !TASK_STATUSES.includes(task.status)) {
|
|
3010
|
+
throw new Error(`${label}.status must be one of: ${TASK_STATUSES.join(", ")}`);
|
|
3011
|
+
}
|
|
3012
|
+
if (typeof task.priority !== "string" || !TASK_PRIORITIES.includes(task.priority)) {
|
|
3013
|
+
throw new Error(`${label}.priority must be one of: ${TASK_PRIORITIES.join(", ")}`);
|
|
3014
|
+
}
|
|
3015
|
+
for (const field of [
|
|
3016
|
+
"agent_id",
|
|
3017
|
+
"assigned_to",
|
|
3018
|
+
"session_id",
|
|
3019
|
+
"working_dir",
|
|
3020
|
+
"locked_by",
|
|
3021
|
+
"approved_by",
|
|
3022
|
+
"recurrence_rule",
|
|
3023
|
+
"recurrence_parent_id",
|
|
3024
|
+
"spawns_template_id",
|
|
3025
|
+
"reason",
|
|
3026
|
+
"spawned_from_session",
|
|
3027
|
+
"assigned_by",
|
|
3028
|
+
"created_by",
|
|
3029
|
+
"assigned_from_project",
|
|
3030
|
+
"task_type",
|
|
3031
|
+
"delegated_from",
|
|
3032
|
+
"runner_id",
|
|
3033
|
+
"current_step"
|
|
3034
|
+
]) {
|
|
3035
|
+
responseNullableString(task[field], `${label}.${field}`);
|
|
3036
|
+
}
|
|
3037
|
+
responseStringArray(task.tags, `${label}.tags`);
|
|
3038
|
+
responseRecord(task.metadata, `${label}.metadata`);
|
|
3039
|
+
for (const field of [
|
|
3040
|
+
"version",
|
|
3041
|
+
"cost_tokens",
|
|
3042
|
+
"cost_usd",
|
|
3043
|
+
"delegation_depth",
|
|
3044
|
+
"retry_count",
|
|
3045
|
+
"max_retries"
|
|
3046
|
+
]) {
|
|
3047
|
+
responseNumber(task[field], `${label}.${field}`);
|
|
3048
|
+
}
|
|
3049
|
+
for (const field of ["estimated_minutes", "actual_minutes", "confidence", "sla_minutes", "total_steps"]) {
|
|
3050
|
+
if (task[field] !== null)
|
|
3051
|
+
responseNumber(task[field], `${label}.${field}`);
|
|
3052
|
+
}
|
|
3053
|
+
responseBoolean(task.requires_approval, `${label}.requires_approval`);
|
|
3054
|
+
responseDateTime(task.created_at, `${label}.created_at`);
|
|
3055
|
+
responseDateTime(task.updated_at, `${label}.updated_at`);
|
|
3056
|
+
for (const field of [
|
|
3057
|
+
"locked_at",
|
|
3058
|
+
"started_at",
|
|
3059
|
+
"completed_at",
|
|
3060
|
+
"due_at",
|
|
3061
|
+
"approved_at",
|
|
3062
|
+
"retry_after",
|
|
3063
|
+
"runner_started_at",
|
|
3064
|
+
"runner_completed_at"
|
|
3065
|
+
]) {
|
|
3066
|
+
responseNullableDateTime(task[field], `${label}.${field}`);
|
|
3067
|
+
}
|
|
3068
|
+
responseOptionalNullableString(task, "machine_id", label);
|
|
3069
|
+
responseOptionalNullableDateTime(task, "synced_at", label);
|
|
3070
|
+
responseOptionalNullableDateTime(task, "archived_at", label);
|
|
3071
|
+
return task;
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
3074
|
+
function responseReceipt(value, expectation, planRevision, taskIds) {
|
|
3075
|
+
const receipt = responseRecord(value, "receipt");
|
|
3076
|
+
exactResponseKeys(receipt, [
|
|
3077
|
+
"schema_version",
|
|
3078
|
+
"receipt_id",
|
|
3079
|
+
"idempotency_key",
|
|
3080
|
+
"plan_id",
|
|
3081
|
+
"project_id",
|
|
3082
|
+
"prior_plan_project_id",
|
|
3083
|
+
"prior_task_project_ids",
|
|
3084
|
+
"task_ids",
|
|
3085
|
+
"task_count",
|
|
3086
|
+
"result_plan_revision",
|
|
3087
|
+
"result_digest",
|
|
3088
|
+
"rollback_supported",
|
|
3089
|
+
"created_at"
|
|
3090
|
+
], "receipt");
|
|
3091
|
+
if (receipt.schema_version !== PLAN_PROJECT_LINK_SCHEMA_VERSION) {
|
|
3092
|
+
throw new Error(`receipt.schema_version must be ${PLAN_PROJECT_LINK_SCHEMA_VERSION}`);
|
|
3093
|
+
}
|
|
3094
|
+
const expectedReceiptId = planProjectLinkReceiptId(expectation.idempotency_key);
|
|
3095
|
+
if (responseString(receipt.receipt_id, "receipt.receipt_id") !== expectedReceiptId) {
|
|
3096
|
+
throw new Error("receipt.receipt_id must match the deterministic apply receipt identity");
|
|
3097
|
+
}
|
|
3098
|
+
if (responseString(receipt.idempotency_key, "receipt.idempotency_key") !== expectation.idempotency_key) {
|
|
3099
|
+
throw new Error("receipt.idempotency_key must match the apply request");
|
|
3100
|
+
}
|
|
3101
|
+
if (responseString(receipt.plan_id, "receipt.plan_id") !== expectation.plan_id) {
|
|
3102
|
+
throw new Error("receipt.plan_id must match the requested plan");
|
|
3103
|
+
}
|
|
3104
|
+
if (responseString(receipt.project_id, "receipt.project_id") !== expectation.project_id) {
|
|
3105
|
+
throw new Error("receipt.project_id must match the requested project");
|
|
3106
|
+
}
|
|
3107
|
+
responseNullableString(receipt.prior_plan_project_id, "receipt.prior_plan_project_id");
|
|
3108
|
+
const priorTaskProjectIds = responseRecord(receipt.prior_task_project_ids, "receipt.prior_task_project_ids");
|
|
3109
|
+
for (const [taskId, projectId] of Object.entries(priorTaskProjectIds)) {
|
|
3110
|
+
responseString(taskId, "receipt.prior_task_project_ids key");
|
|
3111
|
+
responseNullableString(projectId, `receipt.prior_task_project_ids.${taskId}`);
|
|
3112
|
+
}
|
|
3113
|
+
if (!Array.isArray(receipt.task_ids) || receipt.task_ids.some((id) => typeof id !== "string" || id.length === 0)) {
|
|
3114
|
+
throw new Error("receipt.task_ids must be an array of non-empty strings");
|
|
3115
|
+
}
|
|
3116
|
+
const receiptTaskIds = receipt.task_ids;
|
|
3117
|
+
if (new Set(receiptTaskIds).size !== receiptTaskIds.length) {
|
|
3118
|
+
throw new Error("receipt.task_ids must not contain duplicates");
|
|
3119
|
+
}
|
|
3120
|
+
if (receiptTaskIds.length !== taskIds.length || receiptTaskIds.some((taskId, index) => taskId !== taskIds[index])) {
|
|
3121
|
+
throw new Error("receipt.task_ids must exactly match the response task identities");
|
|
3122
|
+
}
|
|
3123
|
+
const priorTaskIds = Object.keys(priorTaskProjectIds).sort();
|
|
3124
|
+
if (priorTaskIds.length !== taskIds.length || priorTaskIds.some((taskId, index) => taskId !== [...taskIds].sort()[index])) {
|
|
3125
|
+
throw new Error("receipt.prior_task_project_ids must exactly cover the response task identities");
|
|
3126
|
+
}
|
|
3127
|
+
if (!Number.isInteger(receipt.task_count) || receipt.task_count !== taskIds.length) {
|
|
3128
|
+
throw new Error("receipt.task_count must equal the response task count");
|
|
3129
|
+
}
|
|
3130
|
+
if (responseString(receipt.result_plan_revision, "receipt.result_plan_revision") !== planRevision) {
|
|
3131
|
+
throw new Error("receipt.result_plan_revision must equal plan.updated_at");
|
|
3132
|
+
}
|
|
3133
|
+
const resultDigest = responseString(receipt.result_digest, "receipt.result_digest");
|
|
3134
|
+
if (!/^[a-f0-9]{64}$/.test(resultDigest)) {
|
|
3135
|
+
throw new Error("receipt.result_digest must be a lowercase SHA-256 digest");
|
|
3136
|
+
}
|
|
3137
|
+
if (receipt.rollback_supported !== true) {
|
|
3138
|
+
throw new Error("receipt.rollback_supported must be true");
|
|
3139
|
+
}
|
|
3140
|
+
responseDateTime(receipt.created_at, "receipt.created_at");
|
|
3141
|
+
return receipt;
|
|
3142
|
+
}
|
|
3143
|
+
function assertPlanProjectLinkResponse(value, expectation) {
|
|
3144
|
+
const response = responseRecord(value, `${expectation.mode} response`);
|
|
3145
|
+
exactResponseKeys(response, ["mode", "action", "plan", "project", "tasks", "receipt"], `${expectation.mode} response`);
|
|
3146
|
+
if (response.mode !== expectation.mode) {
|
|
3147
|
+
throw new Error(`mode must be ${expectation.mode}`);
|
|
3148
|
+
}
|
|
3149
|
+
const allowedActions = expectation.mode === "plan" ? ["would_link", "already_linked"] : ["linked", "already_linked"];
|
|
3150
|
+
if (typeof response.action !== "string" || !allowedActions.includes(response.action)) {
|
|
3151
|
+
throw new Error(`action must be one of: ${allowedActions.join(", ")}`);
|
|
3152
|
+
}
|
|
3153
|
+
const plan = responsePlan(response.plan, expectation.plan_id);
|
|
3154
|
+
responseProject(response.project, expectation.project_id);
|
|
3155
|
+
const tasks = responseTasks(response.tasks, expectation.plan_id);
|
|
3156
|
+
const linkedResult = expectation.mode === "apply" || response.action === "already_linked";
|
|
3157
|
+
if (linkedResult) {
|
|
3158
|
+
if (plan.project_id !== expectation.project_id) {
|
|
3159
|
+
throw new Error("plan.project_id must match the requested project after linkage");
|
|
3160
|
+
}
|
|
3161
|
+
if (tasks.some((task) => task.project_id !== expectation.project_id)) {
|
|
3162
|
+
throw new Error("every task.project_id must match the requested project after linkage");
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
if (expectation.mode === "plan") {
|
|
3166
|
+
if (response.receipt !== null)
|
|
3167
|
+
throw new Error("receipt must be null for a plan response");
|
|
3168
|
+
} else {
|
|
3169
|
+
if (!expectation.idempotency_key)
|
|
3170
|
+
throw new Error("apply validation requires the request idempotency key");
|
|
3171
|
+
const receipt = responseReceipt(response.receipt, expectation, plan.updated_at, tasks.map((task) => task.id));
|
|
3172
|
+
const expectedDigest = planProjectLinkResultDigest(plan, tasks);
|
|
3173
|
+
if (receipt.result_digest !== expectedDigest) {
|
|
3174
|
+
throw new Error("receipt.result_digest must match the returned plan and tasks");
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
return value;
|
|
3178
|
+
}
|
|
3179
|
+
function assertPlanProjectLinkRollbackResponse(value, expectation) {
|
|
3180
|
+
const response = responseRecord(value, "rollback response");
|
|
3181
|
+
exactResponseKeys(response, [
|
|
3182
|
+
"schema_version",
|
|
3183
|
+
"action",
|
|
3184
|
+
"plan",
|
|
3185
|
+
"tasks",
|
|
3186
|
+
"accepted_receipt_id",
|
|
3187
|
+
"rollback_receipt_id",
|
|
3188
|
+
"restored_at"
|
|
3189
|
+
], "rollback response");
|
|
3190
|
+
if (response.schema_version !== PLAN_PROJECT_LINK_SCHEMA_VERSION) {
|
|
3191
|
+
throw new Error(`schema_version must be ${PLAN_PROJECT_LINK_SCHEMA_VERSION}`);
|
|
3192
|
+
}
|
|
3193
|
+
if (response.action !== "restored")
|
|
3194
|
+
throw new Error("action must be restored");
|
|
3195
|
+
const plan = responsePlan(response.plan, expectation.plan_id);
|
|
3196
|
+
responseTasks(response.tasks, expectation.plan_id);
|
|
3197
|
+
if (responseString(response.accepted_receipt_id, "accepted_receipt_id") !== expectation.receipt_id) {
|
|
3198
|
+
throw new Error("accepted_receipt_id must match the rollback request");
|
|
3199
|
+
}
|
|
3200
|
+
const expectedRollbackReceiptId = planProjectLinkRollbackReceiptId(expectation.receipt_id);
|
|
3201
|
+
if (responseString(response.rollback_receipt_id, "rollback_receipt_id") !== expectedRollbackReceiptId) {
|
|
3202
|
+
throw new Error("rollback_receipt_id must match the deterministic rollback receipt identity");
|
|
3203
|
+
}
|
|
3204
|
+
const restoredAt = responseDateTime(response.restored_at, "restored_at");
|
|
3205
|
+
if (plan.updated_at !== restoredAt) {
|
|
3206
|
+
throw new Error("restored_at must equal plan.updated_at");
|
|
3207
|
+
}
|
|
3208
|
+
return value;
|
|
3209
|
+
}
|
|
3210
|
+
var PLAN_PROJECT_LINK_SCHEMA_VERSION = "todos.plan-project-link.v1", PlanProjectLinkError;
|
|
3211
|
+
var init_plan_project_link_contract = __esm(() => {
|
|
3212
|
+
init_types();
|
|
3213
|
+
PlanProjectLinkError = class PlanProjectLinkError extends Error {
|
|
3214
|
+
code;
|
|
3215
|
+
details;
|
|
3216
|
+
constructor(code, message, details = {}) {
|
|
3217
|
+
super(message);
|
|
3218
|
+
this.code = code;
|
|
3219
|
+
this.details = details;
|
|
3220
|
+
this.name = "PlanProjectLinkError";
|
|
3221
|
+
}
|
|
3222
|
+
};
|
|
3223
|
+
});
|
|
3224
|
+
|
|
2825
3225
|
// src/pr-groups/types.ts
|
|
2826
3226
|
var PR_GROUP_LEDGER_SCHEMA_VERSION = 1, PR_GROUP_REPAIR_CYCLE_LIMIT = 2, PrGroupLedgerError;
|
|
2827
3227
|
var init_types2 = __esm(() => {
|
|
@@ -2838,9 +3238,9 @@ var init_types2 = __esm(() => {
|
|
|
2838
3238
|
});
|
|
2839
3239
|
|
|
2840
3240
|
// src/pr-groups/ledger.ts
|
|
2841
|
-
import { createHash as
|
|
3241
|
+
import { createHash as createHash3 } from "crypto";
|
|
2842
3242
|
function sha256(value) {
|
|
2843
|
-
return
|
|
3243
|
+
return createHash3("sha256").update(value).digest("hex");
|
|
2844
3244
|
}
|
|
2845
3245
|
function stableValue(value) {
|
|
2846
3246
|
if (Array.isArray(value))
|
|
@@ -5376,6 +5776,12 @@ function toListQuery(filter = {}) {
|
|
|
5376
5776
|
query["offset"] = filter.offset;
|
|
5377
5777
|
return query;
|
|
5378
5778
|
}
|
|
5779
|
+
function priorityRank(priority) {
|
|
5780
|
+
return PRIORITY_RANK[priority ?? ""] ?? 4;
|
|
5781
|
+
}
|
|
5782
|
+
function compareCloudTaskOrder(a, b) {
|
|
5783
|
+
return priorityRank(a.priority) - priorityRank(b.priority) || b.created_at.localeCompare(a.created_at) || a.id.localeCompare(b.id);
|
|
5784
|
+
}
|
|
5379
5785
|
async function fetchListTagsCapability(client) {
|
|
5380
5786
|
const document = await requiredRemoteRoute(client, "/v1/openapi.json", () => client.transport.get("/openapi.json"));
|
|
5381
5787
|
if (!document || typeof document !== "object" || Array.isArray(document))
|
|
@@ -5397,13 +5803,40 @@ async function requireTagsFilterCapability(client) {
|
|
|
5397
5803
|
throw new Error(`REMOTE_TAGS_FILTER_UNSUPPORTED: configured Todos authority ${authority} does not advertise the tags ` + "query param on GET /v1/tasks; deploy the current @hasna/todos /v1 server to filter by tag; " + "no unfiltered task read was issued");
|
|
5398
5804
|
}
|
|
5399
5805
|
}
|
|
5400
|
-
async function
|
|
5401
|
-
if (filter.tags?.length)
|
|
5402
|
-
await requireTagsFilterCapability(client);
|
|
5806
|
+
async function requestCloudTaskPage(client, filter) {
|
|
5403
5807
|
const res = await requiredRemoteRoute(client, "/v1/tasks", () => client.list("tasks", { query: toListQuery(filter) }));
|
|
5404
5808
|
const envelope = res.raw;
|
|
5405
5809
|
return Array.isArray(envelope?.tasks) ? envelope.tasks : res.items;
|
|
5406
5810
|
}
|
|
5811
|
+
async function cloudListTasks(client, filter = {}) {
|
|
5812
|
+
if (filter.tags?.length)
|
|
5813
|
+
await requireTagsFilterCapability(client);
|
|
5814
|
+
const statuses = Array.isArray(filter.status) ? filter.status : undefined;
|
|
5815
|
+
if (!statuses)
|
|
5816
|
+
return requestCloudTaskPage(client, filter);
|
|
5817
|
+
if (statuses.length === 0)
|
|
5818
|
+
return [];
|
|
5819
|
+
if (statuses.length === 1) {
|
|
5820
|
+
return requestCloudTaskPage(client, { ...filter, status: statuses[0] });
|
|
5821
|
+
}
|
|
5822
|
+
const { status: _status, limit, offset, ...baseFilter } = filter;
|
|
5823
|
+
const start = offset ?? 0;
|
|
5824
|
+
const windowEnd = typeof limit === "number" ? start + limit : undefined;
|
|
5825
|
+
const pages = await Promise.all(statuses.map((status) => requestCloudTaskPage(client, {
|
|
5826
|
+
...baseFilter,
|
|
5827
|
+
status,
|
|
5828
|
+
...windowEnd === undefined ? {} : { limit: windowEnd }
|
|
5829
|
+
})));
|
|
5830
|
+
const seen = new Set;
|
|
5831
|
+
const union = pages.flat().filter((task) => {
|
|
5832
|
+
if (seen.has(task.id))
|
|
5833
|
+
return false;
|
|
5834
|
+
seen.add(task.id);
|
|
5835
|
+
return true;
|
|
5836
|
+
});
|
|
5837
|
+
union.sort(compareCloudTaskOrder);
|
|
5838
|
+
return union.slice(start, windowEnd);
|
|
5839
|
+
}
|
|
5407
5840
|
async function cloudResolveTaskRef(client, ref) {
|
|
5408
5841
|
const input = ref.trim().toLowerCase();
|
|
5409
5842
|
if (!input)
|
|
@@ -5711,17 +6144,50 @@ async function cloudRollbackProjectTaskListEnsure(client, projectId, input) {
|
|
|
5711
6144
|
return requiredRemoteRoute(client, "/v1/projects/:id/task-list/rollback", () => client.transport.post(`/projects/${encodeURIComponent(projectId)}/task-list/rollback`, input), ["PROJECT_NOT_FOUND", "PROJECT_TASK_LIST_RECEIPT_NOT_FOUND"]);
|
|
5712
6145
|
}
|
|
5713
6146
|
async function cloudPlanPlanProjectLink(client, planId, projectId) {
|
|
5714
|
-
|
|
6147
|
+
const route = `/v1/plans/${encodeURIComponent(planId)}/project-link`;
|
|
6148
|
+
const response = await requiredRemoteRoute(client, "/v1/plans/:id/project-link", () => client.transport.get(`/plans/${encodeURIComponent(planId)}/project-link`, { query: { project_id: projectId } }), ["PLAN_PROJECT_LINK_PLAN_NOT_FOUND", "PLAN_PROJECT_LINK_PROJECT_NOT_FOUND"]);
|
|
6149
|
+
try {
|
|
6150
|
+
return assertPlanProjectLinkResponse(response, { mode: "plan", plan_id: planId, project_id: projectId });
|
|
6151
|
+
} catch (error) {
|
|
6152
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
6153
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned an invalid ${PLAN_PROJECT_LINK_SCHEMA_VERSION} plan response: ` + `${reason}; local SQLite fallback is disabled`, { cause: error });
|
|
6154
|
+
}
|
|
5715
6155
|
}
|
|
5716
6156
|
async function cloudApplyPlanProjectLink(client, planId, projectId, input) {
|
|
5717
|
-
|
|
6157
|
+
const route = `/v1/plans/${encodeURIComponent(planId)}/project-link`;
|
|
6158
|
+
const normalizedInput = {
|
|
6159
|
+
...input,
|
|
6160
|
+
idempotency_key: normalizePlanProjectLinkIdempotencyKey(input.idempotency_key)
|
|
6161
|
+
};
|
|
6162
|
+
const response = await requiredRemoteRoute(client, "/v1/plans/:id/project-link", () => client.transport.post(`/plans/${encodeURIComponent(planId)}/project-link`, { project_id: projectId, ...normalizedInput }), ["PLAN_PROJECT_LINK_PLAN_NOT_FOUND", "PLAN_PROJECT_LINK_PROJECT_NOT_FOUND"]);
|
|
6163
|
+
try {
|
|
6164
|
+
return assertPlanProjectLinkResponse(response, {
|
|
6165
|
+
mode: "apply",
|
|
6166
|
+
plan_id: planId,
|
|
6167
|
+
project_id: projectId,
|
|
6168
|
+
idempotency_key: normalizedInput.idempotency_key
|
|
6169
|
+
});
|
|
6170
|
+
} catch (error) {
|
|
6171
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
6172
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned an invalid ${PLAN_PROJECT_LINK_SCHEMA_VERSION} apply response: ` + `${reason}; local SQLite fallback is disabled`, { cause: error });
|
|
6173
|
+
}
|
|
5718
6174
|
}
|
|
5719
6175
|
async function cloudRollbackPlanProjectLink(client, planId, projectId, input) {
|
|
5720
|
-
|
|
6176
|
+
const route = `/v1/plans/${encodeURIComponent(planId)}/project-link/rollback`;
|
|
6177
|
+
const response = await requiredRemoteRoute(client, "/v1/plans/:id/project-link/rollback", () => client.transport.post(`/plans/${encodeURIComponent(planId)}/project-link/rollback`, { project_id: projectId, ...input }), [
|
|
5721
6178
|
"PLAN_PROJECT_LINK_PLAN_NOT_FOUND",
|
|
5722
6179
|
"PLAN_PROJECT_LINK_PROJECT_NOT_FOUND",
|
|
5723
6180
|
"PLAN_PROJECT_LINK_RECEIPT_NOT_FOUND"
|
|
5724
6181
|
]);
|
|
6182
|
+
try {
|
|
6183
|
+
return assertPlanProjectLinkRollbackResponse(response, {
|
|
6184
|
+
plan_id: planId,
|
|
6185
|
+
receipt_id: input.receipt_id
|
|
6186
|
+
});
|
|
6187
|
+
} catch (error) {
|
|
6188
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
6189
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned an invalid ${PLAN_PROJECT_LINK_SCHEMA_VERSION} rollback response: ` + `${reason}; local SQLite fallback is disabled`, { cause: error });
|
|
6190
|
+
}
|
|
5725
6191
|
}
|
|
5726
6192
|
async function cloudListPlans(client, projectId) {
|
|
5727
6193
|
const query = projectId ? { project_id: projectId } : {};
|
|
@@ -5908,17 +6374,105 @@ async function cloudFindCommit(client, sha) {
|
|
|
5908
6374
|
const env = raw ?? {};
|
|
5909
6375
|
return env.commit ?? null;
|
|
5910
6376
|
}
|
|
6377
|
+
function parseCloudTaskGitRef(value, route) {
|
|
6378
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6379
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned a non-object git ref; local SQLite fallback is disabled`);
|
|
6380
|
+
}
|
|
6381
|
+
const ref = value;
|
|
6382
|
+
const requiredStrings = ["id", "task_id", "name", "created_at", "updated_at"];
|
|
6383
|
+
if (requiredStrings.some((field) => typeof ref[field] !== "string" || !ref[field].trim())) {
|
|
6384
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned an incomplete git ref identity; local SQLite fallback is disabled`);
|
|
6385
|
+
}
|
|
6386
|
+
if (ref["ref_type"] !== "branch" && ref["ref_type"] !== "pull_request") {
|
|
6387
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned an invalid git ref type; local SQLite fallback is disabled`);
|
|
6388
|
+
}
|
|
6389
|
+
for (const field of ["url", "provider"]) {
|
|
6390
|
+
if (ref[field] !== null && typeof ref[field] !== "string") {
|
|
6391
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned an invalid git ref ${field}; local SQLite fallback is disabled`);
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
if (!ref["metadata"] || typeof ref["metadata"] !== "object" || Array.isArray(ref["metadata"])) {
|
|
6395
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned invalid git ref metadata; local SQLite fallback is disabled`);
|
|
6396
|
+
}
|
|
6397
|
+
return ref;
|
|
6398
|
+
}
|
|
6399
|
+
function parseCloudTaskGitRefEnvelope(raw, route) {
|
|
6400
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
6401
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned a non-object git ref envelope; local SQLite fallback is disabled`);
|
|
6402
|
+
}
|
|
6403
|
+
const envelope = raw;
|
|
6404
|
+
if (!Array.isArray(envelope["refs"]) || !Number.isSafeInteger(envelope["count"]) || envelope["count"] !== envelope["refs"].length) {
|
|
6405
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned an incomplete git ref envelope; local SQLite fallback is disabled`);
|
|
6406
|
+
}
|
|
6407
|
+
return envelope["refs"].map((ref) => parseCloudTaskGitRef(ref, route));
|
|
6408
|
+
}
|
|
6409
|
+
function openApiHasOperation(document, path, method) {
|
|
6410
|
+
if (!document || typeof document !== "object" || Array.isArray(document))
|
|
6411
|
+
return false;
|
|
6412
|
+
const paths = document["paths"];
|
|
6413
|
+
if (!paths || typeof paths !== "object" || Array.isArray(paths))
|
|
6414
|
+
return false;
|
|
6415
|
+
const route = paths[path];
|
|
6416
|
+
return Boolean(route && typeof route === "object" && !Array.isArray(route) && route[method] && typeof route[method] === "object");
|
|
6417
|
+
}
|
|
6418
|
+
async function fetchGitRefCapabilities(client) {
|
|
6419
|
+
const document = await requiredRemoteRoute(client, "/v1/openapi.json", () => client.transport.get("/openapi.json"));
|
|
6420
|
+
const supported = new Set;
|
|
6421
|
+
if (openApiHasOperation(document, "/v1/tasks/{id}/refs", "get"))
|
|
6422
|
+
supported.add("task-read");
|
|
6423
|
+
if (openApiHasOperation(document, "/v1/tasks/{id}/refs", "post"))
|
|
6424
|
+
supported.add("task-write");
|
|
6425
|
+
if (openApiHasOperation(document, "/v1/refs/{ref}", "get"))
|
|
6426
|
+
supported.add("reverse-read");
|
|
6427
|
+
return supported;
|
|
6428
|
+
}
|
|
6429
|
+
async function requireGitRefCapabilities(client, required) {
|
|
6430
|
+
const authority = remoteAuthorityBase(client);
|
|
6431
|
+
let capabilities = gitRefCapabilityCache.get(authority);
|
|
6432
|
+
if (!capabilities) {
|
|
6433
|
+
capabilities = fetchGitRefCapabilities(client);
|
|
6434
|
+
gitRefCapabilityCache.set(authority, capabilities);
|
|
6435
|
+
}
|
|
6436
|
+
const supported = await capabilities;
|
|
6437
|
+
const missing = required.filter((capability) => !supported.has(capability));
|
|
6438
|
+
if (missing.length > 0) {
|
|
6439
|
+
throw new Error(`REMOTE_GIT_REF_UNSUPPORTED: configured Todos authority ${authority} does not advertise the complete git-ref ` + `contract (${missing.join(", ")} missing); deploy the current @hasna/todos /v1 server before retrying; ` + "no ref mutation or local SQLite fallback was attempted");
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
function sameCloudTaskGitRef(ref, expected) {
|
|
6443
|
+
return ref.id === expected.id && ref.task_id === expected.task_id && ref.ref_type === expected.ref_type && ref.name === expected.name;
|
|
6444
|
+
}
|
|
6445
|
+
async function cloudListTaskRefs(client, taskId) {
|
|
6446
|
+
await requireGitRefCapabilities(client, ["task-read"]);
|
|
6447
|
+
const route = `/v1/tasks/${encodeURIComponent(taskId)}/refs`;
|
|
6448
|
+
const raw = await requiredRemoteRoute(client, route, () => client.transport.get(`/tasks/${encodeURIComponent(taskId)}/refs`));
|
|
6449
|
+
return parseCloudTaskGitRefEnvelope(raw, route);
|
|
6450
|
+
}
|
|
5911
6451
|
async function cloudLinkRef(client, taskId, input) {
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
6452
|
+
await requireGitRefCapabilities(client, ["task-read", "task-write", "reverse-read"]);
|
|
6453
|
+
const route = `/v1/tasks/${encodeURIComponent(taskId)}/refs`;
|
|
6454
|
+
const raw = await requiredRemoteRoute(client, route, () => client.transport.post(`/tasks/${encodeURIComponent(taskId)}/refs`, input));
|
|
6455
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw) || Object.keys(raw).length !== 1 || !("ref" in raw)) {
|
|
6456
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned a non-authoritative git ref response envelope; ` + "local SQLite fallback is disabled");
|
|
6457
|
+
}
|
|
6458
|
+
const linked = parseCloudTaskGitRef(raw.ref, route);
|
|
6459
|
+
if (linked.task_id !== taskId || linked.ref_type !== input.ref_type || linked.name !== input.name) {
|
|
6460
|
+
throw new Error(`REMOTE_API_INCOMPATIBLE: ${route} returned a different git ref identity; local SQLite fallback is disabled`);
|
|
6461
|
+
}
|
|
6462
|
+
const [taskRefs, reverseRefs] = await Promise.all([
|
|
6463
|
+
cloudListTaskRefs(client, taskId),
|
|
6464
|
+
cloudFindRefs(client, input.name)
|
|
6465
|
+
]);
|
|
6466
|
+
if (!taskRefs.some((ref) => sameCloudTaskGitRef(ref, linked)) || !reverseRefs.some((ref) => sameCloudTaskGitRef(ref, linked))) {
|
|
6467
|
+
throw new Error(`REMOTE_REF_PERSISTENCE_UNVERIFIED: configured Todos authority ${remoteAuthorityBase(client)} accepted ${route} ` + "but authoritative task and reverse readback did not return the linked ref; no success line or local SQLite " + "fallback is permitted");
|
|
5915
6468
|
}
|
|
5916
|
-
return
|
|
6469
|
+
return linked;
|
|
5917
6470
|
}
|
|
5918
6471
|
async function cloudFindRefs(client, ref) {
|
|
5919
|
-
|
|
5920
|
-
const
|
|
5921
|
-
|
|
6472
|
+
await requireGitRefCapabilities(client, ["reverse-read"]);
|
|
6473
|
+
const route = `/v1/refs/${encodeURIComponent(ref)}`;
|
|
6474
|
+
const raw = await requiredRemoteRoute(client, route, () => client.transport.get(`/refs/${encodeURIComponent(ref)}`));
|
|
6475
|
+
return parseCloudTaskGitRefEnvelope(raw, route);
|
|
5922
6476
|
}
|
|
5923
6477
|
async function cloudResolvePlan(client, ref, projectId) {
|
|
5924
6478
|
const normalizedRef = ref.toLowerCase();
|
|
@@ -6076,9 +6630,6 @@ async function cloudRecordVerification(client, id, input) {
|
|
|
6076
6630
|
}
|
|
6077
6631
|
return raw;
|
|
6078
6632
|
}
|
|
6079
|
-
function priorityRank(priority) {
|
|
6080
|
-
return PRIORITY_RANK[priority ?? ""] ?? 4;
|
|
6081
|
-
}
|
|
6082
6633
|
async function cloudActiveTasks(client, filter = {}) {
|
|
6083
6634
|
const [pending, inProgress] = await Promise.all([
|
|
6084
6635
|
cloudListTasks(client, { ...filter, status: "pending" }),
|
|
@@ -6392,10 +6943,11 @@ async function cloudTimeline(client, options = {}) {
|
|
|
6392
6943
|
const limit = options.limit ?? 50;
|
|
6393
6944
|
return { entries: entries.slice(offset, offset + limit), total, limit, offset };
|
|
6394
6945
|
}
|
|
6395
|
-
var UUID_RE, TRANSPORT_TOKENS, COMPLETION_EVIDENCE_FIELDS, completionCapabilityCache, SERVER_MODE_CANDIDATES, cachedServerMode = null, listTagsCapabilityCache, RELATION_HYDRATION_CONCURRENCY = 6
|
|
6946
|
+
var UUID_RE, TRANSPORT_TOKENS, COMPLETION_EVIDENCE_FIELDS, completionCapabilityCache, gitRefCapabilityCache, SERVER_MODE_CANDIDATES, cachedServerMode = null, PRIORITY_RANK, listTagsCapabilityCache, RELATION_HYDRATION_CONCURRENCY = 6;
|
|
6396
6947
|
var init_cloud_router = __esm(() => {
|
|
6397
6948
|
init_types();
|
|
6398
6949
|
init_redaction();
|
|
6950
|
+
init_plan_project_link_contract();
|
|
6399
6951
|
init_http_client();
|
|
6400
6952
|
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6401
6953
|
TRANSPORT_TOKENS = {
|
|
@@ -6416,9 +6968,10 @@ var init_cloud_router = __esm(() => {
|
|
|
6416
6968
|
"confidence"
|
|
6417
6969
|
];
|
|
6418
6970
|
completionCapabilityCache = new Map;
|
|
6971
|
+
gitRefCapabilityCache = new Map;
|
|
6419
6972
|
SERVER_MODE_CANDIDATES = ["postgres", "cloud", "self_hosted"];
|
|
6420
|
-
listTagsCapabilityCache = new Map;
|
|
6421
6973
|
PRIORITY_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
6974
|
+
listTagsCapabilityCache = new Map;
|
|
6422
6975
|
});
|
|
6423
6976
|
|
|
6424
6977
|
// src/cli/stage-a.ts
|
|
@@ -12815,7 +13368,7 @@ __export(exports_event_hooks, {
|
|
|
12815
13368
|
emitLocalEventHooks: () => emitLocalEventHooks,
|
|
12816
13369
|
LOCAL_EVENT_TYPES: () => LOCAL_EVENT_TYPES
|
|
12817
13370
|
});
|
|
12818
|
-
import { createHash as
|
|
13371
|
+
import { createHash as createHash4, randomUUID } from "crypto";
|
|
12819
13372
|
import { appendFileSync, mkdirSync as mkdirSync4 } from "fs";
|
|
12820
13373
|
import { dirname as dirname4, resolve as resolve7 } from "path";
|
|
12821
13374
|
import { createConnection } from "net";
|
|
@@ -12882,7 +13435,7 @@ function buildEnvelope(type, payload, timestamp2 = new Date().toISOString()) {
|
|
|
12882
13435
|
payload: redactValue(payload ?? {}),
|
|
12883
13436
|
source: { package: "@hasna/todos", local_only: true }
|
|
12884
13437
|
};
|
|
12885
|
-
const digest =
|
|
13438
|
+
const digest = createHash4("sha256").update(canonicalEvent(base)).digest("hex");
|
|
12886
13439
|
return { ...base, integrity: { algorithm: "sha256", digest } };
|
|
12887
13440
|
}
|
|
12888
13441
|
function summarize(value) {
|
|
@@ -18096,7 +18649,7 @@ var init_boards = __esm(() => {
|
|
|
18096
18649
|
});
|
|
18097
18650
|
|
|
18098
18651
|
// src/lib/artifact-store.ts
|
|
18099
|
-
import { createHash as
|
|
18652
|
+
import { createHash as createHash5 } from "crypto";
|
|
18100
18653
|
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync3, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
18101
18654
|
import { basename as basename2, dirname as dirname5, join as join7, resolve as resolve8 } from "path";
|
|
18102
18655
|
import { tmpdir as tmpdir3 } from "os";
|
|
@@ -18121,7 +18674,7 @@ function artifactStorePath(relativePath) {
|
|
|
18121
18674
|
return join7(artifactStoreRoot(), normalized);
|
|
18122
18675
|
}
|
|
18123
18676
|
function sha2562(buffer) {
|
|
18124
|
-
return
|
|
18677
|
+
return createHash5("sha256").update(buffer).digest("hex");
|
|
18125
18678
|
}
|
|
18126
18679
|
function isTextLike(buffer, path) {
|
|
18127
18680
|
if (buffer.includes(0))
|
|
@@ -20953,6 +21506,14 @@ async function cloudDetailRelations(cloud, id) {
|
|
|
20953
21506
|
return { dependencies: [], blocked_by: [], blocks: [] };
|
|
20954
21507
|
}
|
|
20955
21508
|
}
|
|
21509
|
+
async function cloudDetailGitRefs(cloud, id) {
|
|
21510
|
+
try {
|
|
21511
|
+
return await cloudListTaskRefs(cloud, id);
|
|
21512
|
+
} catch (e) {
|
|
21513
|
+
console.error(chalk3.dim(`Warning: could not verify task git refs: ${e instanceof Error ? e.message : String(e)}`));
|
|
21514
|
+
return null;
|
|
21515
|
+
}
|
|
21516
|
+
}
|
|
20956
21517
|
function resolveProjectIdOrSlug(input) {
|
|
20957
21518
|
const db = getDatabase();
|
|
20958
21519
|
if (isPathLike(input)) {
|
|
@@ -21525,7 +22086,8 @@ function registerTaskCommands(program2) {
|
|
|
21525
22086
|
}
|
|
21526
22087
|
const creatorFilterActive = Boolean(filter["created_by"] || filter["not_created_by"]);
|
|
21527
22088
|
const requestedLimit = filter["limit"];
|
|
21528
|
-
const
|
|
22089
|
+
const combinesScalarStatusPages = Boolean(cloud && Array.isArray(filter["status"]) && filter["status"].length > 1);
|
|
22090
|
+
const reordersAfterQuery = Boolean(opts.sort) || combinesScalarStatusPages;
|
|
21529
22091
|
const narrowsAfterQuery = Boolean(opts.dueToday) || Boolean(opts.overdue) || creatorFilterActive && cloud;
|
|
21530
22092
|
const withholdLimit = requestedLimit !== undefined && (reordersAfterQuery || narrowsAfterQuery);
|
|
21531
22093
|
const scanCeiling = cloud && (withholdLimit || requestedLimit === undefined) ? Math.max(requestedLimit ?? 0, listScanLimit()) : undefined;
|
|
@@ -21676,8 +22238,11 @@ function registerTaskCommands(program2) {
|
|
|
21676
22238
|
let task2;
|
|
21677
22239
|
if (cloud) {
|
|
21678
22240
|
const remote = await cloudGetTask(cloud, await resolveTaskIdForCommand(id, cloud));
|
|
21679
|
-
const commentPage = remote ? await
|
|
21680
|
-
|
|
22241
|
+
const [commentPage, relations, gitRefs] = remote ? await Promise.all([
|
|
22242
|
+
cloudListComments(cloud, remote.id, page.request),
|
|
22243
|
+
cloudDetailRelations(cloud, remote.id),
|
|
22244
|
+
cloudDetailGitRefs(cloud, remote.id)
|
|
22245
|
+
]) : [null, null, null];
|
|
21681
22246
|
task2 = remote ? {
|
|
21682
22247
|
subtasks: [],
|
|
21683
22248
|
...remote,
|
|
@@ -21685,6 +22250,7 @@ function registerTaskCommands(program2) {
|
|
|
21685
22250
|
dependencies: relations.dependencies,
|
|
21686
22251
|
blocked_by: relations.blocked_by,
|
|
21687
22252
|
blocks: relations.blocks,
|
|
22253
|
+
git_refs: gitRefs,
|
|
21688
22254
|
comments: commentPage.comments,
|
|
21689
22255
|
comments_page: {
|
|
21690
22256
|
count: commentPage.count,
|
|
@@ -21697,6 +22263,8 @@ function registerTaskCommands(program2) {
|
|
|
21697
22263
|
} else {
|
|
21698
22264
|
const resolvedId = resolveTaskId(id);
|
|
21699
22265
|
task2 = applyLocalCommentPage(getTaskWithRelations(resolvedId), page);
|
|
22266
|
+
const { getTaskGitRefs: getTaskGitRefs2 } = await Promise.resolve().then(() => (init_task_commits(), exports_task_commits));
|
|
22267
|
+
task2.git_refs = getTaskGitRefs2(resolvedId);
|
|
21700
22268
|
}
|
|
21701
22269
|
if (!task2) {
|
|
21702
22270
|
handleError(new Error(`Task not found: ${id}`));
|
|
@@ -21809,8 +22377,11 @@ function registerTaskCommands(program2) {
|
|
|
21809
22377
|
let task2;
|
|
21810
22378
|
if (cloud) {
|
|
21811
22379
|
const remote = await cloudGetTask(cloud, resolvedId);
|
|
21812
|
-
const commentPage = remote ? await
|
|
21813
|
-
|
|
22380
|
+
const [commentPage, relations, gitRefs] = remote ? await Promise.all([
|
|
22381
|
+
cloudListComments(cloud, remote.id, page.request),
|
|
22382
|
+
cloudDetailRelations(cloud, remote.id),
|
|
22383
|
+
cloudDetailGitRefs(cloud, remote.id)
|
|
22384
|
+
]) : [null, null, null];
|
|
21814
22385
|
task2 = remote ? {
|
|
21815
22386
|
subtasks: [],
|
|
21816
22387
|
checklist: [],
|
|
@@ -21819,6 +22390,7 @@ function registerTaskCommands(program2) {
|
|
|
21819
22390
|
dependencies: relations.dependencies,
|
|
21820
22391
|
blocked_by: relations.blocked_by,
|
|
21821
22392
|
blocks: relations.blocks,
|
|
22393
|
+
git_refs: gitRefs,
|
|
21822
22394
|
comments: commentPage.comments,
|
|
21823
22395
|
comments_page: {
|
|
21824
22396
|
count: commentPage.count,
|
|
@@ -21836,7 +22408,7 @@ function registerTaskCommands(program2) {
|
|
|
21836
22408
|
}
|
|
21837
22409
|
if (globalOpts.json && !cloud) {
|
|
21838
22410
|
const { listTaskFiles: listTaskFiles2 } = await Promise.resolve().then(() => (init_task_files(), exports_task_files));
|
|
21839
|
-
const { getTaskCommits: getTaskCommits2 } = await Promise.resolve().then(() => (init_task_commits(), exports_task_commits));
|
|
22411
|
+
const { getTaskCommits: getTaskCommits2, getTaskGitRefs: getTaskGitRefs2 } = await Promise.resolve().then(() => (init_task_commits(), exports_task_commits));
|
|
21840
22412
|
try {
|
|
21841
22413
|
task2.files = listTaskFiles2(task2.id);
|
|
21842
22414
|
} catch (e) {
|
|
@@ -21847,6 +22419,11 @@ function registerTaskCommands(program2) {
|
|
|
21847
22419
|
} catch (e) {
|
|
21848
22420
|
console.error(chalk3.dim(`Warning: could not load task commits: ${e instanceof Error ? e.message : String(e)}`));
|
|
21849
22421
|
}
|
|
22422
|
+
try {
|
|
22423
|
+
task2.git_refs = getTaskGitRefs2(task2.id);
|
|
22424
|
+
} catch (e) {
|
|
22425
|
+
console.error(chalk3.dim(`Warning: could not load task git refs: ${e instanceof Error ? e.message : String(e)}`));
|
|
22426
|
+
}
|
|
21850
22427
|
output(task2, true);
|
|
21851
22428
|
return;
|
|
21852
22429
|
}
|
|
@@ -22840,62 +23417,6 @@ var init_plan_artifacts = __esm(() => {
|
|
|
22840
23417
|
init_tasks();
|
|
22841
23418
|
});
|
|
22842
23419
|
|
|
22843
|
-
// src/lib/plan-project-link-contract.ts
|
|
22844
|
-
import { createHash as createHash5 } from "crypto";
|
|
22845
|
-
function canonicalPlanProjectLinkJson(value) {
|
|
22846
|
-
if (value === null || typeof value !== "object")
|
|
22847
|
-
return JSON.stringify(value);
|
|
22848
|
-
if (Array.isArray(value))
|
|
22849
|
-
return `[${value.map(canonicalPlanProjectLinkJson).join(",")}]`;
|
|
22850
|
-
return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${canonicalPlanProjectLinkJson(item)}`).join(",")}}`;
|
|
22851
|
-
}
|
|
22852
|
-
function planProjectLinkDigest(value) {
|
|
22853
|
-
return createHash5("sha256").update(canonicalPlanProjectLinkJson(value)).digest("hex");
|
|
22854
|
-
}
|
|
22855
|
-
function normalizePlanProjectLinkIdempotencyKey(value) {
|
|
22856
|
-
const key = value?.trim() ?? "";
|
|
22857
|
-
if (key.length < 8 || key.length > 128 || !/^[A-Za-z0-9._:-]+$/.test(key)) {
|
|
22858
|
-
throw new PlanProjectLinkError("PLAN_PROJECT_LINK_IDEMPOTENCY_KEY_INVALID", "idempotency_key must be 8-128 ASCII letters, digits, dots, underscores, colons, or hyphens");
|
|
22859
|
-
}
|
|
22860
|
-
return key;
|
|
22861
|
-
}
|
|
22862
|
-
function planProjectLinkReceiptId(idempotencyKey) {
|
|
22863
|
-
return `pplr_${planProjectLinkDigest({ idempotency_key: idempotencyKey }).slice(0, 48)}`;
|
|
22864
|
-
}
|
|
22865
|
-
function planProjectLinkRollbackReceiptId(receiptId) {
|
|
22866
|
-
return `pplr_inverse_${planProjectLinkDigest({ accepted_receipt_id: receiptId }).slice(0, 38)}`;
|
|
22867
|
-
}
|
|
22868
|
-
function planProjectLinkRequestHash(planId, projectId) {
|
|
22869
|
-
return planProjectLinkDigest({ plan_id: planId, project_id: projectId });
|
|
22870
|
-
}
|
|
22871
|
-
function planProjectLinkResultDigest(plan, tasks) {
|
|
22872
|
-
return planProjectLinkDigest({
|
|
22873
|
-
plan_id: plan.id,
|
|
22874
|
-
plan_project_id: plan.project_id,
|
|
22875
|
-
tasks: tasks.map((task) => ({ id: task.id, plan_id: task.plan_id, project_id: task.project_id })).sort((left, right) => left.id.localeCompare(right.id))
|
|
22876
|
-
});
|
|
22877
|
-
}
|
|
22878
|
-
function assertPlanProjectLinkReceipt(value) {
|
|
22879
|
-
const receipt = value;
|
|
22880
|
-
if (!receipt || typeof receipt !== "object" || receipt.schema_version !== PLAN_PROJECT_LINK_SCHEMA_VERSION || typeof receipt.receipt_id !== "string" || typeof receipt.idempotency_key !== "string" || typeof receipt.plan_id !== "string" || typeof receipt.project_id !== "string" || !Array.isArray(receipt.task_ids) || receipt.task_ids.some((id) => typeof id !== "string") || !receipt.prior_task_project_ids || typeof receipt.prior_task_project_ids !== "object" || typeof receipt.result_digest !== "string") {
|
|
22881
|
-
throw new PlanProjectLinkError("PLAN_PROJECT_LINK_RECEIPT_NOT_FOUND", "Stored plan-project-link receipt is invalid");
|
|
22882
|
-
}
|
|
22883
|
-
return receipt;
|
|
22884
|
-
}
|
|
22885
|
-
var PLAN_PROJECT_LINK_SCHEMA_VERSION = "todos.plan-project-link.v1", PlanProjectLinkError;
|
|
22886
|
-
var init_plan_project_link_contract = __esm(() => {
|
|
22887
|
-
PlanProjectLinkError = class PlanProjectLinkError extends Error {
|
|
22888
|
-
code;
|
|
22889
|
-
details;
|
|
22890
|
-
constructor(code, message, details = {}) {
|
|
22891
|
-
super(message);
|
|
22892
|
-
this.code = code;
|
|
22893
|
-
this.details = details;
|
|
22894
|
-
this.name = "PlanProjectLinkError";
|
|
22895
|
-
}
|
|
22896
|
-
};
|
|
22897
|
-
});
|
|
22898
|
-
|
|
22899
23420
|
// src/lib/plan-project-link.ts
|
|
22900
23421
|
async function exactPlanProjectLinkState(store, planId, projectId) {
|
|
22901
23422
|
const [plan, project] = await Promise.all([
|
|
@@ -40339,6 +40860,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
40339
40860
|
ProjectTaskListEnsureResult: projectTaskListEnsureResultSchema,
|
|
40340
40861
|
ProjectTaskListRollbackResult: projectTaskListRollbackResultSchema,
|
|
40341
40862
|
TaskComment: taskCommentSchema,
|
|
40863
|
+
TaskGitRef: taskGitRefSchema,
|
|
40342
40864
|
Plan: planSchema,
|
|
40343
40865
|
PlanProjectLinkReceipt: planProjectLinkReceiptSchema,
|
|
40344
40866
|
PlanProjectLinkResult: planProjectLinkResultSchema,
|
|
@@ -41500,6 +42022,92 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
41500
42022
|
}
|
|
41501
42023
|
}
|
|
41502
42024
|
},
|
|
42025
|
+
"/v1/tasks/{id}/refs": {
|
|
42026
|
+
get: {
|
|
42027
|
+
operationId: "listTaskGitRefs",
|
|
42028
|
+
summary: "List git branch and pull-request refs linked to a task",
|
|
42029
|
+
parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
|
|
42030
|
+
responses: {
|
|
42031
|
+
"200": {
|
|
42032
|
+
content: {
|
|
42033
|
+
"application/json": {
|
|
42034
|
+
schema: {
|
|
42035
|
+
type: "object",
|
|
42036
|
+
additionalProperties: false,
|
|
42037
|
+
required: ["refs", "count"],
|
|
42038
|
+
properties: {
|
|
42039
|
+
refs: { type: "array", items: { $ref: "#/components/schemas/TaskGitRef" } },
|
|
42040
|
+
count: { type: "integer", minimum: 0 }
|
|
42041
|
+
}
|
|
42042
|
+
}
|
|
42043
|
+
}
|
|
42044
|
+
}
|
|
42045
|
+
}
|
|
42046
|
+
}
|
|
42047
|
+
},
|
|
42048
|
+
post: {
|
|
42049
|
+
operationId: "linkTaskGitRef",
|
|
42050
|
+
summary: "Link a git branch or pull-request ref to a task",
|
|
42051
|
+
parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
|
|
42052
|
+
requestBody: {
|
|
42053
|
+
required: true,
|
|
42054
|
+
content: {
|
|
42055
|
+
"application/json": {
|
|
42056
|
+
schema: {
|
|
42057
|
+
type: "object",
|
|
42058
|
+
additionalProperties: false,
|
|
42059
|
+
required: ["ref_type", "name"],
|
|
42060
|
+
properties: {
|
|
42061
|
+
ref_type: { type: "string", enum: ["branch", "pull_request"] },
|
|
42062
|
+
name: { type: "string", minLength: 1 },
|
|
42063
|
+
url: { type: "string" },
|
|
42064
|
+
provider: { type: "string" },
|
|
42065
|
+
metadata: { type: "object", additionalProperties: true }
|
|
42066
|
+
}
|
|
42067
|
+
}
|
|
42068
|
+
}
|
|
42069
|
+
}
|
|
42070
|
+
},
|
|
42071
|
+
responses: {
|
|
42072
|
+
"201": {
|
|
42073
|
+
content: {
|
|
42074
|
+
"application/json": {
|
|
42075
|
+
schema: {
|
|
42076
|
+
type: "object",
|
|
42077
|
+
additionalProperties: false,
|
|
42078
|
+
required: ["ref"],
|
|
42079
|
+
properties: { ref: { $ref: "#/components/schemas/TaskGitRef" } }
|
|
42080
|
+
}
|
|
42081
|
+
}
|
|
42082
|
+
}
|
|
42083
|
+
}
|
|
42084
|
+
}
|
|
42085
|
+
}
|
|
42086
|
+
},
|
|
42087
|
+
"/v1/refs/{ref}": {
|
|
42088
|
+
get: {
|
|
42089
|
+
operationId: "findTaskGitRefs",
|
|
42090
|
+
summary: "Find task links by git branch or pull-request ref",
|
|
42091
|
+
parameters: [{ name: "ref", in: "path", required: true, schema: { type: "string" } }],
|
|
42092
|
+
responses: {
|
|
42093
|
+
"200": {
|
|
42094
|
+
content: {
|
|
42095
|
+
"application/json": {
|
|
42096
|
+
schema: {
|
|
42097
|
+
type: "object",
|
|
42098
|
+
additionalProperties: false,
|
|
42099
|
+
required: ["refs", "count"],
|
|
42100
|
+
properties: {
|
|
42101
|
+
refs: { type: "array", items: { $ref: "#/components/schemas/TaskGitRef" } },
|
|
42102
|
+
count: { type: "integer", minimum: 0 }
|
|
42103
|
+
}
|
|
42104
|
+
}
|
|
42105
|
+
}
|
|
42106
|
+
}
|
|
42107
|
+
}
|
|
42108
|
+
}
|
|
42109
|
+
}
|
|
42110
|
+
},
|
|
41503
42111
|
"/v1/tasks/{id}/start": {
|
|
41504
42112
|
post: {
|
|
41505
42113
|
operationId: "startTask",
|
|
@@ -41951,7 +42559,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
41951
42559
|
}
|
|
41952
42560
|
};
|
|
41953
42561
|
}
|
|
41954
|
-
var taskSchema, projectSchema, taskListSchema, projectTaskListEnsureReceiptSchema, projectTaskListEnsureResultSchema, projectTaskListRollbackResultSchema, taskCommentSchema, planSchema, planProjectLinkReceiptSchema, planProjectLinkResultSchema, planProjectLinkRollbackResultSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
|
|
42562
|
+
var taskSchema, projectSchema, taskListSchema, projectTaskListEnsureReceiptSchema, projectTaskListEnsureResultSchema, projectTaskListRollbackResultSchema, taskCommentSchema, taskGitRefSchema, planSchema, planProjectLinkReceiptSchema, planProjectLinkResultSchema, planProjectLinkRollbackResultSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
|
|
41955
42563
|
var init_openapi = __esm(() => {
|
|
41956
42564
|
init_package_version();
|
|
41957
42565
|
init_types();
|
|
@@ -42087,6 +42695,32 @@ var init_openapi = __esm(() => {
|
|
|
42087
42695
|
created_at: { type: "string", format: "date-time" }
|
|
42088
42696
|
}
|
|
42089
42697
|
};
|
|
42698
|
+
taskGitRefSchema = {
|
|
42699
|
+
type: "object",
|
|
42700
|
+
additionalProperties: false,
|
|
42701
|
+
required: [
|
|
42702
|
+
"id",
|
|
42703
|
+
"task_id",
|
|
42704
|
+
"ref_type",
|
|
42705
|
+
"name",
|
|
42706
|
+
"url",
|
|
42707
|
+
"provider",
|
|
42708
|
+
"metadata",
|
|
42709
|
+
"created_at",
|
|
42710
|
+
"updated_at"
|
|
42711
|
+
],
|
|
42712
|
+
properties: {
|
|
42713
|
+
id: { type: "string", minLength: 1 },
|
|
42714
|
+
task_id: { type: "string", minLength: 1 },
|
|
42715
|
+
ref_type: { type: "string", enum: ["branch", "pull_request"] },
|
|
42716
|
+
name: { type: "string", minLength: 1 },
|
|
42717
|
+
url: { type: "string", nullable: true },
|
|
42718
|
+
provider: { type: "string", nullable: true },
|
|
42719
|
+
metadata: { type: "object", additionalProperties: true },
|
|
42720
|
+
created_at: { type: "string", format: "date-time" },
|
|
42721
|
+
updated_at: { type: "string", format: "date-time" }
|
|
42722
|
+
}
|
|
42723
|
+
};
|
|
42090
42724
|
planSchema = {
|
|
42091
42725
|
type: "object",
|
|
42092
42726
|
required: ["id", "slug", "name", "status", "created_at", "updated_at"],
|