opencode-ship 1.1.0 → 1.1.1-rc.9

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/plugin.js CHANGED
@@ -1,208 +1,10 @@
1
- // opencode-ship v1.1.0
1
+ // opencode-ship v1.1.1-rc.9
2
2
  var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
- };
7
3
  var __export = (target, all) => {
8
4
  for (var name in all)
9
5
  __defProp(target, name, { get: all[name], enumerable: true });
10
6
  };
11
7
 
12
- // src/profile.js
13
- function isValidProfile(name) {
14
- return typeof name === "string" && PROFILES.includes(name);
15
- }
16
- var PROFILES;
17
- var init_profile = __esm({
18
- "src/profile.js"() {
19
- PROFILES = Object.freeze(["engineering"]);
20
- }
21
- });
22
-
23
- // src/installer/json-pointer.js
24
- function isObject2(v) {
25
- return v !== null && typeof v === "object" && !Array.isArray(v);
26
- }
27
- function stableStringify(value) {
28
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
29
- if (isObject2(value)) {
30
- const keys = Object.keys(value).sort();
31
- return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
32
- }
33
- return JSON.stringify(value);
34
- }
35
- function canonicalJson(value) {
36
- return stableStringify(value);
37
- }
38
- var init_json_pointer = __esm({
39
- "src/installer/json-pointer.js"() {
40
- }
41
- });
42
-
43
- // src/installer/hash.js
44
- import { createHash as createHash8 } from "node:crypto";
45
- function bytesHash(buffer) {
46
- return createHash8("sha256").update(buffer).digest("hex");
47
- }
48
- function bytesHashString(text) {
49
- return bytesHash(Buffer.from(text, "utf8"));
50
- }
51
- var init_hash = __esm({
52
- "src/installer/hash.js"() {
53
- init_json_pointer();
54
- }
55
- });
56
-
57
- // src/installer/lock.js
58
- var lock_exports = {};
59
- __export(lock_exports, {
60
- CURRENT_LOCK_SCHEMA: () => CURRENT_LOCK_SCHEMA,
61
- computeIntegrity: () => computeIntegrity,
62
- lockPath: () => lockPath,
63
- lockSchemaRevision: () => lockSchemaRevision,
64
- migrateLegacyLock: () => migrateLegacyLock,
65
- readLock: () => readLock2,
66
- readValidatedLock: () => readValidatedLock,
67
- validateIntegrity: () => validateIntegrity,
68
- validateLock: () => validateLock,
69
- writeLock: () => writeLock
70
- });
71
- import { readFile as readFile11, writeFile as writeFile10, rename as rename4, mkdir as mkdir12 } from "node:fs/promises";
72
- import { existsSync as existsSync10 } from "node:fs";
73
- import { dirname as dirname6, resolve as resolve10 } from "node:path";
74
- function lockSchemaRevision() {
75
- return CURRENT_LOCK_SCHEMA;
76
- }
77
- function lockPath(repoRoot) {
78
- return resolve10(repoRoot, ".opencode", "ship.lock.json");
79
- }
80
- async function readLock2(repoRoot) {
81
- const path = lockPath(repoRoot);
82
- if (!existsSync10(path)) return null;
83
- try {
84
- const raw = await readFile11(path, "utf8");
85
- const parsed = JSON.parse(raw);
86
- return parsed;
87
- } catch {
88
- return null;
89
- }
90
- }
91
- async function writeLock(repoRoot, lock) {
92
- const path = lockPath(repoRoot);
93
- await mkdir12(dirname6(path), { recursive: true });
94
- const integrity = computeIntegrity(lock);
95
- const finalLock = { ...lock, integrity };
96
- const raw = JSON.stringify(finalLock, null, 2) + "\n";
97
- const tmp = `${path}.tmp`;
98
- await writeFile10(tmp, raw, "utf8");
99
- await rename4(tmp, path);
100
- return path;
101
- }
102
- function computeIntegrity(lock) {
103
- const { integrity: _ignored, ...without } = lock ?? {};
104
- void _ignored;
105
- return {
106
- lockSha256: bytesHashString(stableStringify(without))
107
- };
108
- }
109
- async function validateIntegrity(lock) {
110
- if (!lock?.integrity?.lockSha256) return false;
111
- const expected = computeIntegrity(lock).lockSha256;
112
- return expected === lock.integrity.lockSha256;
113
- }
114
- function validateLock(rawLock) {
115
- if (rawLock === null || rawLock === void 0) {
116
- return { ok: true, kind: "missing", issues: [] };
117
- }
118
- if (typeof rawLock !== "object" || Array.isArray(rawLock)) {
119
- return { ok: false, kind: "shape", issues: ["lock root must be an object"] };
120
- }
121
- const issues = [];
122
- let kind = "ok";
123
- if (rawLock.contractVersion !== CURRENT_LOCK_SCHEMA && rawLock.contractVersion !== 1 && rawLock.contractVersion !== 2) {
124
- issues.push(`unsupported contractVersion: ${JSON.stringify(rawLock.contractVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 2, or 1)`);
125
- kind = "schema";
126
- }
127
- const manager = rawLock.manager;
128
- if (manager === void 0) {
129
- issues.push("manager section missing");
130
- kind = kind === "ok" ? "shape" : kind;
131
- } else if (typeof manager !== "object" || manager === null) {
132
- issues.push("manager section must be an object");
133
- kind = kind === "ok" ? "shape" : kind;
134
- } else if (manager.schemaVersion !== CURRENT_LOCK_SCHEMA && manager.schemaVersion !== 2 && manager.schemaVersion !== 1) {
135
- issues.push(`unsupported manager.schemaVersion: ${JSON.stringify(manager.schemaVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 2, or 1)`);
136
- kind = "schema";
137
- } else if (manager.name !== "opencode-ship") {
138
- issues.push(`unknown manager.name: ${JSON.stringify(manager.name)}`);
139
- kind = "shape";
140
- } else if (rawLock.contractVersion >= 2 && manager.schemaVersion >= 2 && manager.profile !== void 0 && !isValidProfile(manager.profile)) {
141
- issues.push(`invalid manager.profile: ${JSON.stringify(manager.profile)} (expected one of: core, engineering)`);
142
- kind = "shape";
143
- }
144
- if (!rawLock.files || !Array.isArray(rawLock.files)) {
145
- issues.push("files must be an array");
146
- kind = kind === "ok" ? "shape" : kind;
147
- }
148
- if (!rawLock.integrity || typeof rawLock.integrity !== "object") {
149
- issues.push("integrity section missing");
150
- kind = kind === "ok" ? "shape" : kind;
151
- } else {
152
- const expected = computeIntegrity(rawLock).lockSha256;
153
- if (expected !== rawLock.integrity.lockSha256) {
154
- issues.push(`integrity mismatch: stored ${rawLock.integrity.lockSha256} != computed ${expected}`);
155
- kind = "integrity";
156
- }
157
- }
158
- return { ok: issues.length === 0, kind, issues };
159
- }
160
- async function readValidatedLock(repoRoot) {
161
- const path = lockPath(repoRoot);
162
- if (!existsSync10(path)) {
163
- return { kind: "missing", lock: null, issues: [] };
164
- }
165
- let raw;
166
- try {
167
- const text = await readFile11(path, "utf8");
168
- raw = JSON.parse(text);
169
- } catch (e) {
170
- return {
171
- kind: "integrity",
172
- lock: null,
173
- issues: [`unable to parse lock JSON: ${e?.message ?? String(e)}`]
174
- };
175
- }
176
- const validation = validateLock(raw);
177
- return { kind: validation.kind, lock: validation.ok ? raw : null, issues: validation.issues };
178
- }
179
- async function migrateLegacyLock(repoRoot) {
180
- const legacy = resolve10(repoRoot, ".opencode", "delivery.lock.json");
181
- if (!existsSync10(legacy)) return null;
182
- try {
183
- const raw = await readFile11(legacy, "utf8");
184
- const parsed = JSON.parse(raw);
185
- if (parsed.contractVersion !== 1 || typeof parsed.adapterSha256 !== "string") return null;
186
- return {
187
- kind: "legacy-lock",
188
- sourcePath: legacy,
189
- payload: { contractVersion: 1, adapterSha256: parsed.adapterSha256, writtenAt: parsed.writtenAt ?? null },
190
- sha256: bytesHashString(raw)
191
- };
192
- } catch {
193
- return null;
194
- }
195
- }
196
- var CURRENT_LOCK_SCHEMA;
197
- var init_lock = __esm({
198
- "src/installer/lock.js"() {
199
- init_hash();
200
- init_json_pointer();
201
- init_profile();
202
- CURRENT_LOCK_SCHEMA = 3;
203
- }
204
- });
205
-
206
8
  // node_modules/zod/v4/classic/external.js
207
9
  var external_exports = {};
208
10
  __export(external_exports, {
@@ -14211,6 +14013,12 @@ function bucketFor(check2) {
14211
14013
  if (check2.state === "failure") return "fail";
14212
14014
  return "pending";
14213
14015
  }
14016
+ function finalReviewGateSnapshot(manifest) {
14017
+ return {
14018
+ standards: manifest?.finalStandardsReview ?? null,
14019
+ spec: manifest?.finalSpecReview ?? null
14020
+ };
14021
+ }
14214
14022
  function gateSnapshot({ manifest, prHead, checks }) {
14215
14023
  const required2 = manifest.adapter?.ci?.requiredChecks ?? [];
14216
14024
  const observed = checks ?? [];
@@ -14232,6 +14040,7 @@ function gateSnapshot({ manifest, prHead, checks }) {
14232
14040
  prHead: prHead ?? null,
14233
14041
  reviewerSha: manifest?.lastReviewerSha ?? null,
14234
14042
  verifierSha: manifest?.lastVerifierSha ?? null,
14043
+ finalReviews: finalReviewGateSnapshot(manifest ?? {}),
14235
14044
  checks: observed,
14236
14045
  missingChecks: missing,
14237
14046
  failingChecks: failing,
@@ -14241,10 +14050,39 @@ function gateSnapshot({ manifest, prHead, checks }) {
14241
14050
  function checkGates({ manifest, prHead, checks, requires }) {
14242
14051
  const snap = gateSnapshot({ manifest, prHead, checks });
14243
14052
  const need = new Set(requires ?? ["review", "local-verification", "remote-ci"]);
14053
+ const hasDualFinalReview = Boolean(
14054
+ manifest?.finalStandardsReview || manifest?.finalSpecReview
14055
+ );
14244
14056
  if (need.has("review")) {
14245
- if (!manifest?.lastReviewerSha) return { ok: false, reason: "missing-review", snapshot: snap };
14246
- if (manifest.lastReviewerSha !== prHead) {
14247
- return { ok: false, reason: "head-changed-after-review", snapshot: snap };
14057
+ if (hasDualFinalReview) {
14058
+ const standards = manifest?.finalStandardsReview;
14059
+ const spec = manifest?.finalSpecReview;
14060
+ if (!standards || !standards.headSha) {
14061
+ return { ok: false, reason: "missing-final-review", axis: "standards", snapshot: snap };
14062
+ }
14063
+ if (!spec || !spec.headSha) {
14064
+ return { ok: false, reason: "missing-final-review", axis: "spec", snapshot: snap };
14065
+ }
14066
+ if (standards.headSha !== prHead) {
14067
+ return { ok: false, reason: "head-changed-after-final-review", axis: "standards", snapshot: snap };
14068
+ }
14069
+ if (spec.headSha !== prHead) {
14070
+ return { ok: false, reason: "head-changed-after-final-review", axis: "spec", snapshot: snap };
14071
+ }
14072
+ if (standards.headSha !== spec.headSha) {
14073
+ return { ok: false, reason: "final-review-head-mismatch", snapshot: snap };
14074
+ }
14075
+ if ((standards.packageHash ?? null) !== (spec.packageHash ?? null)) {
14076
+ return { ok: false, reason: "final-review-package-mismatch", snapshot: snap };
14077
+ }
14078
+ if ((standards.verdict ?? null) !== "pass" || (spec.verdict ?? null) !== "pass") {
14079
+ return { ok: false, reason: "final-review-failed", snapshot: snap };
14080
+ }
14081
+ } else {
14082
+ if (!manifest?.lastReviewerSha) return { ok: false, reason: "missing-review", snapshot: snap };
14083
+ if (manifest.lastReviewerSha !== prHead) {
14084
+ return { ok: false, reason: "head-changed-after-review", snapshot: snap };
14085
+ }
14248
14086
  }
14249
14087
  }
14250
14088
  if (need.has("local-verification")) {
@@ -14272,6 +14110,17 @@ function gateFailureEnvelope(result) {
14272
14110
  return { kind: "missing-gate", gate: "review" };
14273
14111
  case "missing-verifier":
14274
14112
  return { kind: "missing-gate", gate: "local-verification" };
14113
+ case "missing-final-review":
14114
+ return {
14115
+ kind: "missing-final-review",
14116
+ axis: result.axis
14117
+ };
14118
+ case "final-review-head-mismatch":
14119
+ return { kind: "final-review-head-mismatch" };
14120
+ case "final-review-package-mismatch":
14121
+ return { kind: "final-review-package-mismatch" };
14122
+ case "final-review-failed":
14123
+ return { kind: "final-review-failed" };
14275
14124
  case "head-changed-after-review":
14276
14125
  return {
14277
14126
  kind: "head-changed-after-review",
@@ -14284,6 +14133,12 @@ function gateFailureEnvelope(result) {
14284
14133
  headSha: result.snapshot.prHead ?? "",
14285
14134
  verifierSha: result.snapshot.verifierSha ?? ""
14286
14135
  };
14136
+ case "head-changed-after-final-review":
14137
+ return {
14138
+ kind: "head-changed-after-final-review",
14139
+ axis: result.axis,
14140
+ headSha: result.snapshot.prHead ?? ""
14141
+ };
14287
14142
  case "ci-missing":
14288
14143
  return { kind: "ci-missing", missing: result.snapshot.missingChecks };
14289
14144
  case "ci-failing":
@@ -14918,8 +14773,11 @@ function createPublishTool(deps) {
14918
14773
  };
14919
14774
  }
14920
14775
 
14776
+ // src/profile.js
14777
+ var PROFILES = Object.freeze(["engineering"]);
14778
+ var LEGACY_PROFILES = Object.freeze(["core"]);
14779
+
14921
14780
  // src/installer/engineering-config.js
14922
- init_profile();
14923
14781
  var MODEL_ID_RE = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
14924
14782
  var DEFAULTS = Object.freeze({
14925
14783
  planner: "openai/gpt-5.6-sol",
@@ -15005,8 +14863,23 @@ function createPlanStartTool(deps) {
15005
14863
  };
15006
14864
  }
15007
14865
 
14866
+ // src/installer/json-pointer.js
14867
+ function isObject2(v) {
14868
+ return v !== null && typeof v === "object" && !Array.isArray(v);
14869
+ }
14870
+ function stableStringify(value) {
14871
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
14872
+ if (isObject2(value)) {
14873
+ const keys = Object.keys(value).sort();
14874
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
14875
+ }
14876
+ return JSON.stringify(value);
14877
+ }
14878
+ function canonicalJson(value) {
14879
+ return stableStringify(value);
14880
+ }
14881
+
15008
14882
  // src/workflow/plan.js
15009
- init_json_pointer();
15010
14883
  import { createHash as createHash3 } from "node:crypto";
15011
14884
  function isPlainObject2(v) {
15012
14885
  return v !== null && typeof v === "object" && !Array.isArray(v);
@@ -15463,8 +15336,8 @@ function createRunStartTool(deps) {
15463
15336
  if (!workflowId || !SAFE_ID_RE4.test(workflowId)) {
15464
15337
  return failure("run-start", "workflowId required (safe id)", { operationId: opId, retryable: false });
15465
15338
  }
15466
- const config2 = deps.config ?? null;
15467
- const expectedModels = config2?.value?.workflow?.models ?? null;
15339
+ const configValue = deps.configValue ?? deps.config?.value ?? null;
15340
+ const expectedModels = configValue?.workflow?.models ?? null;
15468
15341
  if (!expectedModels) {
15469
15342
  return failure("run-start", "run-start requires configured workflow.models", { operationId: opId, retryable: false });
15470
15343
  }
@@ -15577,7 +15450,9 @@ var EVENT_KINDS = Object.freeze({
15577
15450
  TASK_REVIEW: "task-review",
15578
15451
  COMMIT: "commit",
15579
15452
  TASK_COMPLETE: "task-complete",
15453
+ ALL_TASKS_DONE: "all-tasks-done",
15580
15454
  FINAL_REVIEW: "final-review",
15455
+ READY_PENDING: "ready-pending",
15581
15456
  READY: "ready",
15582
15457
  MERGE: "merge",
15583
15458
  BLOCKED: "blocked"
@@ -15656,6 +15531,16 @@ function reduce(state, event) {
15656
15531
  event: recorded(EVENT_KINDS.TASK_DISPATCH, { taskId: event.data.taskId, briefHash: event.data.briefHash, round })
15657
15532
  };
15658
15533
  }
15534
+ case EVENT_KINDS.TASK_REPORT: {
15535
+ if (state.state !== STATES2.RUNNING && state.state !== STATES2.FIX_PENDING || state.activeTask === null) {
15536
+ throw new Error(`run reducer: TASK_REPORT requires running with active task`);
15537
+ }
15538
+ ensureActiveTask(state, event.data.taskId);
15539
+ return {
15540
+ state: nextState({ state: STATES2.RUNNING, round: state.round + 1 }),
15541
+ event: recorded(EVENT_KINDS.TASK_REPORT, { taskId: event.data.taskId, reportHash: event.data.reportHash })
15542
+ };
15543
+ }
15659
15544
  case EVENT_KINDS.TASK_REVIEW: {
15660
15545
  if (state.state !== STATES2.RUNNING && state.state !== STATES2.FIX_PENDING || state.activeTask === null) {
15661
15546
  throw new Error(`run reducer: TASK_REVIEW requires running with active task`);
@@ -15693,15 +15578,43 @@ function reduce(state, event) {
15693
15578
  if (state.state !== STATES2.COMMITTED) {
15694
15579
  throw new Error(`run reducer: TASK_COMPLETE requires state=committed, got ${state.state}`);
15695
15580
  }
15581
+ const moreTasks = event.data.moreTasks === false ? false : true;
15582
+ const completedTasks = [...state.completedTasks, state.taskReady?.taskId].filter(Boolean);
15583
+ const nextStateObj = moreTasks ? { state: STATES2.RUNNING, activeTask: null, taskReady: null, round: 0, failures: 0, completedTasks } : { state: STATES2.ALL_TASKS_DONE, activeTask: null, taskReady: null, completedTasks };
15696
15584
  return {
15697
- state: nextState({ state: STATES2.RUNNING, activeTask: null, taskReady: null, round: 0, failures: 0 }),
15698
- event: recorded(EVENT_KINDS.TASK_COMPLETE, { taskId: event.data.taskId })
15585
+ state: nextState(nextStateObj),
15586
+ event: recorded(EVENT_KINDS.TASK_COMPLETE, { taskId: event.data.taskId, moreTasks })
15587
+ };
15588
+ }
15589
+ case EVENT_KINDS.FINAL_REVIEW: {
15590
+ if (state.state !== STATES2.ALL_TASKS_DONE && state.state !== STATES2.READY_PENDING) {
15591
+ throw new Error(`run reducer: FINAL_REVIEW requires state=all-tasks-done|ready-pending, got ${state.state}`);
15592
+ }
15593
+ const nextReadyPending = state.state === STATES2.READY_PENDING ? state : { ...state, state: STATES2.READY_PENDING, finalReview: { [event.data.axis]: event.data.review } };
15594
+ const finalReview = nextReadyPending.finalReview ?? {};
15595
+ finalReview[event.data.axis] = event.data.review;
15596
+ const bothRecorded = finalReview.standards && finalReview.spec;
15597
+ return {
15598
+ state: nextState({
15599
+ state: STATES2.READY_PENDING,
15600
+ finalReview
15601
+ }),
15602
+ event: recorded(EVENT_KINDS.FINAL_REVIEW, {
15603
+ axis: event.data.axis,
15604
+ verdict: event.data.verdict,
15605
+ headSha: event.data.headSha,
15606
+ mergeBaseSha: event.data.mergeBaseSha,
15607
+ packageHash: event.data.packageHash
15608
+ })
15699
15609
  };
15700
15610
  }
15701
15611
  case EVENT_KINDS.READY: {
15702
15612
  if (state.state !== STATES2.COMMITTED && state.state !== STATES2.READY_PENDING) {
15703
15613
  throw new Error(`run reducer: READY requires state=committed|ready-pending, got ${state.state}`);
15704
15614
  }
15615
+ if (state.state === STATES2.READY_PENDING && (!state.finalReview?.standards || !state.finalReview?.spec)) {
15616
+ throw new Error(`run reducer: READY requires both Standards and Spec final reviews`);
15617
+ }
15705
15618
  return {
15706
15619
  state: nextState({ state: STATES2.READY, activeTask: null, completedTasks: state.completedTasks }),
15707
15620
  event: recorded(EVENT_KINDS.READY, { headSha: event.data.headSha })
@@ -16125,8 +16038,8 @@ var ship_config_schema_default = {
16125
16038
  schemaVersion: { enum: [1, 2] },
16126
16039
  profile: {
16127
16040
  type: "string",
16128
- enum: ["engineering"],
16129
- description: "Active profile. Engineering is the only supported profile in 1.1.0."
16041
+ enum: ["engineering", "core"],
16042
+ description: "Active profile. Engineering is the only supported profile in 1.1.0; core is accepted on read for legacy consumer migration."
16130
16043
  },
16131
16044
  owner: {
16132
16045
  type: "string",
@@ -16289,6 +16202,28 @@ var ship_config_schema_default = {
16289
16202
  }
16290
16203
  }
16291
16204
  }
16205
+ },
16206
+ skillDiscovery: {
16207
+ type: "object",
16208
+ additionalProperties: false,
16209
+ description: "Trusted-auto skill discovery policy. Default mode is trusted-auto with the canonical owner allowlist and install-count threshold.",
16210
+ properties: {
16211
+ mode: {
16212
+ type: "string",
16213
+ enum: ["suggest-only", "trusted-auto", "disabled"]
16214
+ },
16215
+ trustedOwners: {
16216
+ type: "array",
16217
+ items: { type: "string", pattern: "^[A-Za-z0-9_.-]+$" }
16218
+ },
16219
+ minInstalls: { type: "integer", minimum: 0 },
16220
+ maxAutoInstall: { type: "integer", minimum: 0, maximum: 20 },
16221
+ blocklist: {
16222
+ type: "array",
16223
+ items: { type: "string", pattern: "^[A-Za-z0-9_./-]+$" }
16224
+ },
16225
+ requireImmutableRef: { type: "boolean" }
16226
+ }
16292
16227
  }
16293
16228
  }
16294
16229
  };
@@ -16394,9 +16329,16 @@ function validateSchema(value, schema) {
16394
16329
  return { ok: issues.length === 0, issues };
16395
16330
  }
16396
16331
 
16332
+ // src/installer/hash.js
16333
+ import { createHash as createHash8 } from "node:crypto";
16334
+ function bytesHash(buffer) {
16335
+ return createHash8("sha256").update(buffer).digest("hex");
16336
+ }
16337
+ function bytesHashString(text) {
16338
+ return bytesHash(Buffer.from(text, "utf8"));
16339
+ }
16340
+
16397
16341
  // src/installer/config.js
16398
- init_json_pointer();
16399
- init_hash();
16400
16342
  function configPath(repoRoot) {
16401
16343
  return resolve9(repoRoot, ".opencode", "ship.config.json");
16402
16344
  }
@@ -16467,8 +16409,23 @@ function renderDefaultConfig(detection, overrides = {}) {
16467
16409
  };
16468
16410
  }
16469
16411
 
16470
- // src/plugin.js
16471
- init_lock();
16412
+ // src/installer/lock.js
16413
+ import { readFile as readFile11, writeFile as writeFile10, rename as rename4, mkdir as mkdir12 } from "node:fs/promises";
16414
+ import { existsSync as existsSync10 } from "node:fs";
16415
+ import { dirname as dirname6, resolve as resolve10 } from "node:path";
16416
+ function lockPath(repoRoot) {
16417
+ return resolve10(repoRoot, ".opencode", "ship.lock.json");
16418
+ }
16419
+ async function readLock2(repoRoot) {
16420
+ const path = lockPath(repoRoot);
16421
+ if (!existsSync10(path)) return null;
16422
+ try {
16423
+ const raw = await readFile11(path, "utf8");
16424
+ return JSON.parse(raw);
16425
+ } catch {
16426
+ return null;
16427
+ }
16428
+ }
16472
16429
 
16473
16430
  // src/installer/detection/project.js
16474
16431
  import { spawnSync as spawnSync5 } from "node:child_process";
@@ -16616,17 +16573,14 @@ function detectProject(repoRoot = process.cwd()) {
16616
16573
  // src/installer/cleanup.js
16617
16574
  import { spawnSync as spawnSync6 } from "node:child_process";
16618
16575
  import { resolve as pathResolve } from "node:path";
16619
- init_json_pointer();
16620
- init_json_pointer();
16621
- init_hash();
16622
- init_lock();
16576
+ import { existsSync as existsSync12, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "node:fs";
16623
16577
  function spawn5(repoRoot, args) {
16624
16578
  const r = spawnSync6("git", ["-C", repoRoot, ...args], { encoding: "utf8" });
16625
16579
  return { status: r.status ?? -1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
16626
16580
  }
16627
16581
  function casDeleteBranch2(repoRoot, branch, expectedSha) {
16628
16582
  const argv = ["update-ref", "-d"];
16629
- if (expectedSha && /^[0-9a-f]{7,}$/i.test(expectedSha)) {
16583
+ if (expectedSha && /^[0-9f]{7,}$/i.test(expectedSha)) {
16630
16584
  argv.push(`refs/heads/${branch}`, expectedSha);
16631
16585
  } else {
16632
16586
  argv.push(`refs/heads/${branch}`);
@@ -16640,19 +16594,26 @@ function safeRemoveWorktree2(repoRoot, target) {
16640
16594
  function worktreeRootOf(adapter) {
16641
16595
  return adapter?.worktree?.root ?? ".worktrees";
16642
16596
  }
16643
- async function loadLockMemo(repoRoot) {
16644
- return readLock2(repoRoot);
16597
+ async function cleanupPendingPath(repoRoot) {
16598
+ const common = await resolveGitCommonDir(repoRoot);
16599
+ return pathResolve(opencodeShipStateDir(common), "cleanup-pending.json");
16645
16600
  }
16646
- async function writeLockMemo(repoRoot, lock) {
16647
- const { writeLock: writer } = await Promise.resolve().then(() => (init_lock(), lock_exports));
16648
- await writer(repoRoot, lock);
16601
+ async function loadCleanupPending(repoRoot) {
16602
+ const path = await cleanupPendingPath(repoRoot);
16603
+ if (!existsSync12(path)) return [];
16604
+ try {
16605
+ const raw = await readFileSync2(path, "utf8");
16606
+ const parsed = JSON.parse(raw);
16607
+ return Array.isArray(parsed) ? parsed : [];
16608
+ } catch {
16609
+ return [];
16610
+ }
16649
16611
  }
16650
- async function appendCleanupPending(repoRoot, entry) {
16651
- const lock = await loadLockMemo(repoRoot);
16652
- if (!lock) return null;
16653
- const next = [...lock.cleanupPending ?? [], entry];
16654
- await writeLockMemo(repoRoot, { ...lock, cleanupPending: dedupePending(next) });
16655
- return lock;
16612
+ async function saveCleanupPending(repoRoot, entries) {
16613
+ const path = await cleanupPendingPath(repoRoot);
16614
+ const dir = pathResolve(path, "..");
16615
+ if (!existsSync12(dir)) mkdirSync(dir, { recursive: true });
16616
+ writeFileSync(path, JSON.stringify(dedupePending(entries), null, 2) + "\n", "utf8");
16656
16617
  }
16657
16618
  function dedupePending(entries) {
16658
16619
  const seen = /* @__PURE__ */ new Set();
@@ -16665,6 +16626,12 @@ function dedupePending(entries) {
16665
16626
  }
16666
16627
  return out;
16667
16628
  }
16629
+ async function appendCleanupPending(repoRoot, entry) {
16630
+ const current = await loadCleanupPending(repoRoot);
16631
+ const next = [...current, entry];
16632
+ await saveCleanupPending(repoRoot, next);
16633
+ return next;
16634
+ }
16668
16635
  function reject(reason, extra = {}) {
16669
16636
  return { ok: false, reason, ...extra };
16670
16637
  }
@@ -16774,10 +16741,10 @@ function flattenShipConfig(ship) {
16774
16741
  }
16775
16742
 
16776
16743
  // src/version.js
16777
- import { readFileSync as readFileSync2, existsSync as existsSync12 } from "node:fs";
16744
+ import { readFileSync as readFileSync3, existsSync as existsSync13 } from "node:fs";
16778
16745
  import { dirname as dirname7, resolve as resolve13 } from "node:path";
16779
16746
  import { fileURLToPath } from "node:url";
16780
- var PACKAGE_VERSION = "1.1.0";
16747
+ var PACKAGE_VERSION = "1.1.1-rc.9";
16781
16748
  var TEMPLATE_SET = `v${PACKAGE_VERSION}`;
16782
16749
 
16783
16750
  // src/plugin.js
@@ -17178,11 +17145,14 @@ var factories = {
17178
17145
  runStart: {
17179
17146
  args: {
17180
17147
  workflowId: tool.schema.string(),
17148
+ revision: tool.schema.number().optional(),
17149
+ sha256: tool.schema.string().optional(),
17181
17150
  operationId: tool.schema.string().optional()
17182
17151
  },
17183
17152
  build: (rt) => createRunStartTool({
17184
17153
  repoRoot: rt.repoRoot,
17185
- owner: rt.owner
17154
+ owner: rt.owner,
17155
+ configValue: rt.configValue
17186
17156
  })
17187
17157
  },
17188
17158
  taskReport: {