ds-harness-remote 0.4.3 → 0.4.4

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 CHANGED
@@ -538,18 +538,18 @@ var ParseStatus = class _ParseStatus {
538
538
  if (this.value !== "aborted")
539
539
  this.value = "aborted";
540
540
  }
541
- static mergeArray(status, results) {
541
+ static mergeArray(status2, results) {
542
542
  const arrayValue = [];
543
543
  for (const s2 of results) {
544
544
  if (s2.status === "aborted")
545
545
  return INVALID;
546
546
  if (s2.status === "dirty")
547
- status.dirty();
547
+ status2.dirty();
548
548
  arrayValue.push(s2.value);
549
549
  }
550
- return { status: status.value, value: arrayValue };
550
+ return { status: status2.value, value: arrayValue };
551
551
  }
552
- static async mergeObjectAsync(status, pairs) {
552
+ static async mergeObjectAsync(status2, pairs) {
553
553
  const syncPairs = [];
554
554
  for (const pair of pairs) {
555
555
  const key = await pair.key;
@@ -559,9 +559,9 @@ var ParseStatus = class _ParseStatus {
559
559
  value
560
560
  });
561
561
  }
562
- return _ParseStatus.mergeObjectSync(status, syncPairs);
562
+ return _ParseStatus.mergeObjectSync(status2, syncPairs);
563
563
  }
564
- static mergeObjectSync(status, pairs) {
564
+ static mergeObjectSync(status2, pairs) {
565
565
  const finalObject = {};
566
566
  for (const pair of pairs) {
567
567
  const { key, value } = pair;
@@ -570,14 +570,14 @@ var ParseStatus = class _ParseStatus {
570
570
  if (value.status === "aborted")
571
571
  return INVALID;
572
572
  if (key.status === "dirty")
573
- status.dirty();
573
+ status2.dirty();
574
574
  if (value.status === "dirty")
575
- status.dirty();
575
+ status2.dirty();
576
576
  if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
577
577
  finalObject[key.value] = value.value;
578
578
  }
579
579
  }
580
- return { status: status.value, value: finalObject };
580
+ return { status: status2.value, value: finalObject };
581
581
  }
582
582
  };
583
583
  var INVALID = Object.freeze({
@@ -1037,7 +1037,7 @@ var ZodString = class _ZodString extends ZodType {
1037
1037
  });
1038
1038
  return INVALID;
1039
1039
  }
1040
- const status = new ParseStatus();
1040
+ const status2 = new ParseStatus();
1041
1041
  let ctx = void 0;
1042
1042
  for (const check of this._def.checks) {
1043
1043
  if (check.kind === "min") {
@@ -1051,7 +1051,7 @@ var ZodString = class _ZodString extends ZodType {
1051
1051
  exact: false,
1052
1052
  message: check.message
1053
1053
  });
1054
- status.dirty();
1054
+ status2.dirty();
1055
1055
  }
1056
1056
  } else if (check.kind === "max") {
1057
1057
  if (input2.data.length > check.value) {
@@ -1064,7 +1064,7 @@ var ZodString = class _ZodString extends ZodType {
1064
1064
  exact: false,
1065
1065
  message: check.message
1066
1066
  });
1067
- status.dirty();
1067
+ status2.dirty();
1068
1068
  }
1069
1069
  } else if (check.kind === "length") {
1070
1070
  const tooBig = input2.data.length > check.value;
@@ -1090,7 +1090,7 @@ var ZodString = class _ZodString extends ZodType {
1090
1090
  message: check.message
1091
1091
  });
1092
1092
  }
1093
- status.dirty();
1093
+ status2.dirty();
1094
1094
  }
1095
1095
  } else if (check.kind === "email") {
1096
1096
  if (!emailRegex.test(input2.data)) {
@@ -1100,7 +1100,7 @@ var ZodString = class _ZodString extends ZodType {
1100
1100
  code: ZodIssueCode.invalid_string,
1101
1101
  message: check.message
1102
1102
  });
1103
- status.dirty();
1103
+ status2.dirty();
1104
1104
  }
1105
1105
  } else if (check.kind === "emoji") {
1106
1106
  if (!emojiRegex) {
@@ -1113,7 +1113,7 @@ var ZodString = class _ZodString extends ZodType {
1113
1113
  code: ZodIssueCode.invalid_string,
1114
1114
  message: check.message
1115
1115
  });
1116
- status.dirty();
1116
+ status2.dirty();
1117
1117
  }
1118
1118
  } else if (check.kind === "uuid") {
1119
1119
  if (!uuidRegex.test(input2.data)) {
@@ -1123,7 +1123,7 @@ var ZodString = class _ZodString extends ZodType {
1123
1123
  code: ZodIssueCode.invalid_string,
1124
1124
  message: check.message
1125
1125
  });
1126
- status.dirty();
1126
+ status2.dirty();
1127
1127
  }
1128
1128
  } else if (check.kind === "nanoid") {
1129
1129
  if (!nanoidRegex.test(input2.data)) {
@@ -1133,7 +1133,7 @@ var ZodString = class _ZodString extends ZodType {
1133
1133
  code: ZodIssueCode.invalid_string,
1134
1134
  message: check.message
1135
1135
  });
1136
- status.dirty();
1136
+ status2.dirty();
1137
1137
  }
1138
1138
  } else if (check.kind === "cuid") {
1139
1139
  if (!cuidRegex.test(input2.data)) {
@@ -1143,7 +1143,7 @@ var ZodString = class _ZodString extends ZodType {
1143
1143
  code: ZodIssueCode.invalid_string,
1144
1144
  message: check.message
1145
1145
  });
1146
- status.dirty();
1146
+ status2.dirty();
1147
1147
  }
1148
1148
  } else if (check.kind === "cuid2") {
1149
1149
  if (!cuid2Regex.test(input2.data)) {
@@ -1153,7 +1153,7 @@ var ZodString = class _ZodString extends ZodType {
1153
1153
  code: ZodIssueCode.invalid_string,
1154
1154
  message: check.message
1155
1155
  });
1156
- status.dirty();
1156
+ status2.dirty();
1157
1157
  }
1158
1158
  } else if (check.kind === "ulid") {
1159
1159
  if (!ulidRegex.test(input2.data)) {
@@ -1163,7 +1163,7 @@ var ZodString = class _ZodString extends ZodType {
1163
1163
  code: ZodIssueCode.invalid_string,
1164
1164
  message: check.message
1165
1165
  });
1166
- status.dirty();
1166
+ status2.dirty();
1167
1167
  }
1168
1168
  } else if (check.kind === "url") {
1169
1169
  try {
@@ -1175,7 +1175,7 @@ var ZodString = class _ZodString extends ZodType {
1175
1175
  code: ZodIssueCode.invalid_string,
1176
1176
  message: check.message
1177
1177
  });
1178
- status.dirty();
1178
+ status2.dirty();
1179
1179
  }
1180
1180
  } else if (check.kind === "regex") {
1181
1181
  check.regex.lastIndex = 0;
@@ -1187,7 +1187,7 @@ var ZodString = class _ZodString extends ZodType {
1187
1187
  code: ZodIssueCode.invalid_string,
1188
1188
  message: check.message
1189
1189
  });
1190
- status.dirty();
1190
+ status2.dirty();
1191
1191
  }
1192
1192
  } else if (check.kind === "trim") {
1193
1193
  input2.data = input2.data.trim();
@@ -1199,7 +1199,7 @@ var ZodString = class _ZodString extends ZodType {
1199
1199
  validation: { includes: check.value, position: check.position },
1200
1200
  message: check.message
1201
1201
  });
1202
- status.dirty();
1202
+ status2.dirty();
1203
1203
  }
1204
1204
  } else if (check.kind === "toLowerCase") {
1205
1205
  input2.data = input2.data.toLowerCase();
@@ -1213,7 +1213,7 @@ var ZodString = class _ZodString extends ZodType {
1213
1213
  validation: { startsWith: check.value },
1214
1214
  message: check.message
1215
1215
  });
1216
- status.dirty();
1216
+ status2.dirty();
1217
1217
  }
1218
1218
  } else if (check.kind === "endsWith") {
1219
1219
  if (!input2.data.endsWith(check.value)) {
@@ -1223,7 +1223,7 @@ var ZodString = class _ZodString extends ZodType {
1223
1223
  validation: { endsWith: check.value },
1224
1224
  message: check.message
1225
1225
  });
1226
- status.dirty();
1226
+ status2.dirty();
1227
1227
  }
1228
1228
  } else if (check.kind === "datetime") {
1229
1229
  const regex = datetimeRegex(check);
@@ -1234,7 +1234,7 @@ var ZodString = class _ZodString extends ZodType {
1234
1234
  validation: "datetime",
1235
1235
  message: check.message
1236
1236
  });
1237
- status.dirty();
1237
+ status2.dirty();
1238
1238
  }
1239
1239
  } else if (check.kind === "date") {
1240
1240
  const regex = dateRegex;
@@ -1245,7 +1245,7 @@ var ZodString = class _ZodString extends ZodType {
1245
1245
  validation: "date",
1246
1246
  message: check.message
1247
1247
  });
1248
- status.dirty();
1248
+ status2.dirty();
1249
1249
  }
1250
1250
  } else if (check.kind === "time") {
1251
1251
  const regex = timeRegex(check);
@@ -1256,7 +1256,7 @@ var ZodString = class _ZodString extends ZodType {
1256
1256
  validation: "time",
1257
1257
  message: check.message
1258
1258
  });
1259
- status.dirty();
1259
+ status2.dirty();
1260
1260
  }
1261
1261
  } else if (check.kind === "duration") {
1262
1262
  if (!durationRegex.test(input2.data)) {
@@ -1266,7 +1266,7 @@ var ZodString = class _ZodString extends ZodType {
1266
1266
  code: ZodIssueCode.invalid_string,
1267
1267
  message: check.message
1268
1268
  });
1269
- status.dirty();
1269
+ status2.dirty();
1270
1270
  }
1271
1271
  } else if (check.kind === "ip") {
1272
1272
  if (!isValidIP(input2.data, check.version)) {
@@ -1276,7 +1276,7 @@ var ZodString = class _ZodString extends ZodType {
1276
1276
  code: ZodIssueCode.invalid_string,
1277
1277
  message: check.message
1278
1278
  });
1279
- status.dirty();
1279
+ status2.dirty();
1280
1280
  }
1281
1281
  } else if (check.kind === "jwt") {
1282
1282
  if (!isValidJWT(input2.data, check.alg)) {
@@ -1286,7 +1286,7 @@ var ZodString = class _ZodString extends ZodType {
1286
1286
  code: ZodIssueCode.invalid_string,
1287
1287
  message: check.message
1288
1288
  });
1289
- status.dirty();
1289
+ status2.dirty();
1290
1290
  }
1291
1291
  } else if (check.kind === "cidr") {
1292
1292
  if (!isValidCidr(input2.data, check.version)) {
@@ -1296,7 +1296,7 @@ var ZodString = class _ZodString extends ZodType {
1296
1296
  code: ZodIssueCode.invalid_string,
1297
1297
  message: check.message
1298
1298
  });
1299
- status.dirty();
1299
+ status2.dirty();
1300
1300
  }
1301
1301
  } else if (check.kind === "base64") {
1302
1302
  if (!base64Regex.test(input2.data)) {
@@ -1306,7 +1306,7 @@ var ZodString = class _ZodString extends ZodType {
1306
1306
  code: ZodIssueCode.invalid_string,
1307
1307
  message: check.message
1308
1308
  });
1309
- status.dirty();
1309
+ status2.dirty();
1310
1310
  }
1311
1311
  } else if (check.kind === "base64url") {
1312
1312
  if (!base64urlRegex.test(input2.data)) {
@@ -1316,13 +1316,13 @@ var ZodString = class _ZodString extends ZodType {
1316
1316
  code: ZodIssueCode.invalid_string,
1317
1317
  message: check.message
1318
1318
  });
1319
- status.dirty();
1319
+ status2.dirty();
1320
1320
  }
1321
1321
  } else {
1322
1322
  util.assertNever(check);
1323
1323
  }
1324
1324
  }
1325
- return { status: status.value, value: input2.data };
1325
+ return { status: status2.value, value: input2.data };
1326
1326
  }
1327
1327
  _regex(regex, validation, message) {
1328
1328
  return this.refinement((data) => regex.test(data), {
@@ -1598,7 +1598,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1598
1598
  return INVALID;
1599
1599
  }
1600
1600
  let ctx = void 0;
1601
- const status = new ParseStatus();
1601
+ const status2 = new ParseStatus();
1602
1602
  for (const check of this._def.checks) {
1603
1603
  if (check.kind === "int") {
1604
1604
  if (!util.isInteger(input2.data)) {
@@ -1609,7 +1609,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1609
1609
  received: "float",
1610
1610
  message: check.message
1611
1611
  });
1612
- status.dirty();
1612
+ status2.dirty();
1613
1613
  }
1614
1614
  } else if (check.kind === "min") {
1615
1615
  const tooSmall = check.inclusive ? input2.data < check.value : input2.data <= check.value;
@@ -1623,7 +1623,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1623
1623
  exact: false,
1624
1624
  message: check.message
1625
1625
  });
1626
- status.dirty();
1626
+ status2.dirty();
1627
1627
  }
1628
1628
  } else if (check.kind === "max") {
1629
1629
  const tooBig = check.inclusive ? input2.data > check.value : input2.data >= check.value;
@@ -1637,7 +1637,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1637
1637
  exact: false,
1638
1638
  message: check.message
1639
1639
  });
1640
- status.dirty();
1640
+ status2.dirty();
1641
1641
  }
1642
1642
  } else if (check.kind === "multipleOf") {
1643
1643
  if (floatSafeRemainder(input2.data, check.value) !== 0) {
@@ -1647,7 +1647,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1647
1647
  multipleOf: check.value,
1648
1648
  message: check.message
1649
1649
  });
1650
- status.dirty();
1650
+ status2.dirty();
1651
1651
  }
1652
1652
  } else if (check.kind === "finite") {
1653
1653
  if (!Number.isFinite(input2.data)) {
@@ -1656,13 +1656,13 @@ var ZodNumber = class _ZodNumber extends ZodType {
1656
1656
  code: ZodIssueCode.not_finite,
1657
1657
  message: check.message
1658
1658
  });
1659
- status.dirty();
1659
+ status2.dirty();
1660
1660
  }
1661
1661
  } else {
1662
1662
  util.assertNever(check);
1663
1663
  }
1664
1664
  }
1665
- return { status: status.value, value: input2.data };
1665
+ return { status: status2.value, value: input2.data };
1666
1666
  }
1667
1667
  gte(value, message) {
1668
1668
  return this.setLimit("min", value, true, errorUtil.toString(message));
@@ -1827,7 +1827,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1827
1827
  return this._getInvalidInput(input2);
1828
1828
  }
1829
1829
  let ctx = void 0;
1830
- const status = new ParseStatus();
1830
+ const status2 = new ParseStatus();
1831
1831
  for (const check of this._def.checks) {
1832
1832
  if (check.kind === "min") {
1833
1833
  const tooSmall = check.inclusive ? input2.data < check.value : input2.data <= check.value;
@@ -1840,7 +1840,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1840
1840
  inclusive: check.inclusive,
1841
1841
  message: check.message
1842
1842
  });
1843
- status.dirty();
1843
+ status2.dirty();
1844
1844
  }
1845
1845
  } else if (check.kind === "max") {
1846
1846
  const tooBig = check.inclusive ? input2.data > check.value : input2.data >= check.value;
@@ -1853,7 +1853,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1853
1853
  inclusive: check.inclusive,
1854
1854
  message: check.message
1855
1855
  });
1856
- status.dirty();
1856
+ status2.dirty();
1857
1857
  }
1858
1858
  } else if (check.kind === "multipleOf") {
1859
1859
  if (input2.data % check.value !== BigInt(0)) {
@@ -1863,13 +1863,13 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1863
1863
  multipleOf: check.value,
1864
1864
  message: check.message
1865
1865
  });
1866
- status.dirty();
1866
+ status2.dirty();
1867
1867
  }
1868
1868
  } else {
1869
1869
  util.assertNever(check);
1870
1870
  }
1871
1871
  }
1872
- return { status: status.value, value: input2.data };
1872
+ return { status: status2.value, value: input2.data };
1873
1873
  }
1874
1874
  _getInvalidInput(input2) {
1875
1875
  const ctx = this._getOrReturnCtx(input2);
@@ -2027,7 +2027,7 @@ var ZodDate = class _ZodDate extends ZodType {
2027
2027
  });
2028
2028
  return INVALID;
2029
2029
  }
2030
- const status = new ParseStatus();
2030
+ const status2 = new ParseStatus();
2031
2031
  let ctx = void 0;
2032
2032
  for (const check of this._def.checks) {
2033
2033
  if (check.kind === "min") {
@@ -2041,7 +2041,7 @@ var ZodDate = class _ZodDate extends ZodType {
2041
2041
  minimum: check.value,
2042
2042
  type: "date"
2043
2043
  });
2044
- status.dirty();
2044
+ status2.dirty();
2045
2045
  }
2046
2046
  } else if (check.kind === "max") {
2047
2047
  if (input2.data.getTime() > check.value) {
@@ -2054,14 +2054,14 @@ var ZodDate = class _ZodDate extends ZodType {
2054
2054
  maximum: check.value,
2055
2055
  type: "date"
2056
2056
  });
2057
- status.dirty();
2057
+ status2.dirty();
2058
2058
  }
2059
2059
  } else {
2060
2060
  util.assertNever(check);
2061
2061
  }
2062
2062
  }
2063
2063
  return {
2064
- status: status.value,
2064
+ status: status2.value,
2065
2065
  value: new Date(input2.data.getTime())
2066
2066
  };
2067
2067
  }
@@ -2247,7 +2247,7 @@ ZodVoid.create = (params) => {
2247
2247
  };
2248
2248
  var ZodArray = class _ZodArray extends ZodType {
2249
2249
  _parse(input2) {
2250
- const { ctx, status } = this._processInputParams(input2);
2250
+ const { ctx, status: status2 } = this._processInputParams(input2);
2251
2251
  const def = this._def;
2252
2252
  if (ctx.parsedType !== ZodParsedType.array) {
2253
2253
  addIssueToContext(ctx, {
@@ -2270,7 +2270,7 @@ var ZodArray = class _ZodArray extends ZodType {
2270
2270
  exact: true,
2271
2271
  message: def.exactLength.message
2272
2272
  });
2273
- status.dirty();
2273
+ status2.dirty();
2274
2274
  }
2275
2275
  }
2276
2276
  if (def.minLength !== null) {
@@ -2283,7 +2283,7 @@ var ZodArray = class _ZodArray extends ZodType {
2283
2283
  exact: false,
2284
2284
  message: def.minLength.message
2285
2285
  });
2286
- status.dirty();
2286
+ status2.dirty();
2287
2287
  }
2288
2288
  }
2289
2289
  if (def.maxLength !== null) {
@@ -2296,20 +2296,20 @@ var ZodArray = class _ZodArray extends ZodType {
2296
2296
  exact: false,
2297
2297
  message: def.maxLength.message
2298
2298
  });
2299
- status.dirty();
2299
+ status2.dirty();
2300
2300
  }
2301
2301
  }
2302
2302
  if (ctx.common.async) {
2303
2303
  return Promise.all([...ctx.data].map((item, i) => {
2304
2304
  return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));
2305
2305
  })).then((result2) => {
2306
- return ParseStatus.mergeArray(status, result2);
2306
+ return ParseStatus.mergeArray(status2, result2);
2307
2307
  });
2308
2308
  }
2309
2309
  const result = [...ctx.data].map((item, i) => {
2310
2310
  return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));
2311
2311
  });
2312
- return ParseStatus.mergeArray(status, result);
2312
+ return ParseStatus.mergeArray(status2, result);
2313
2313
  }
2314
2314
  get element() {
2315
2315
  return this._def.type;
@@ -2398,7 +2398,7 @@ var ZodObject = class _ZodObject extends ZodType {
2398
2398
  });
2399
2399
  return INVALID;
2400
2400
  }
2401
- const { status, ctx } = this._processInputParams(input2);
2401
+ const { status: status2, ctx } = this._processInputParams(input2);
2402
2402
  const { shape, keys: shapeKeys } = this._getCached();
2403
2403
  const extraKeys = [];
2404
2404
  if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
@@ -2433,7 +2433,7 @@ var ZodObject = class _ZodObject extends ZodType {
2433
2433
  code: ZodIssueCode.unrecognized_keys,
2434
2434
  keys: extraKeys
2435
2435
  });
2436
- status.dirty();
2436
+ status2.dirty();
2437
2437
  }
2438
2438
  } else if (unknownKeys === "strip") {
2439
2439
  } else {
@@ -2467,10 +2467,10 @@ var ZodObject = class _ZodObject extends ZodType {
2467
2467
  }
2468
2468
  return syncPairs;
2469
2469
  }).then((syncPairs) => {
2470
- return ParseStatus.mergeObjectSync(status, syncPairs);
2470
+ return ParseStatus.mergeObjectSync(status2, syncPairs);
2471
2471
  });
2472
2472
  } else {
2473
- return ParseStatus.mergeObjectSync(status, pairs);
2473
+ return ParseStatus.mergeObjectSync(status2, pairs);
2474
2474
  }
2475
2475
  }
2476
2476
  get shape() {
@@ -2948,7 +2948,7 @@ function mergeValues(a, b) {
2948
2948
  }
2949
2949
  var ZodIntersection = class extends ZodType {
2950
2950
  _parse(input2) {
2951
- const { status, ctx } = this._processInputParams(input2);
2951
+ const { status: status2, ctx } = this._processInputParams(input2);
2952
2952
  const handleParsed = (parsedLeft, parsedRight) => {
2953
2953
  if (isAborted(parsedLeft) || isAborted(parsedRight)) {
2954
2954
  return INVALID;
@@ -2961,9 +2961,9 @@ var ZodIntersection = class extends ZodType {
2961
2961
  return INVALID;
2962
2962
  }
2963
2963
  if (isDirty(parsedLeft) || isDirty(parsedRight)) {
2964
- status.dirty();
2964
+ status2.dirty();
2965
2965
  }
2966
- return { status: status.value, value: merged.data };
2966
+ return { status: status2.value, value: merged.data };
2967
2967
  };
2968
2968
  if (ctx.common.async) {
2969
2969
  return Promise.all([
@@ -3001,7 +3001,7 @@ ZodIntersection.create = (left, right, params) => {
3001
3001
  };
3002
3002
  var ZodTuple = class _ZodTuple extends ZodType {
3003
3003
  _parse(input2) {
3004
- const { status, ctx } = this._processInputParams(input2);
3004
+ const { status: status2, ctx } = this._processInputParams(input2);
3005
3005
  if (ctx.parsedType !== ZodParsedType.array) {
3006
3006
  addIssueToContext(ctx, {
3007
3007
  code: ZodIssueCode.invalid_type,
@@ -3029,7 +3029,7 @@ var ZodTuple = class _ZodTuple extends ZodType {
3029
3029
  exact: false,
3030
3030
  type: "array"
3031
3031
  });
3032
- status.dirty();
3032
+ status2.dirty();
3033
3033
  }
3034
3034
  const items = [...ctx.data].map((item, itemIndex) => {
3035
3035
  const schema = this._def.items[itemIndex] || this._def.rest;
@@ -3039,10 +3039,10 @@ var ZodTuple = class _ZodTuple extends ZodType {
3039
3039
  }).filter((x) => !!x);
3040
3040
  if (ctx.common.async) {
3041
3041
  return Promise.all(items).then((results) => {
3042
- return ParseStatus.mergeArray(status, results);
3042
+ return ParseStatus.mergeArray(status2, results);
3043
3043
  });
3044
3044
  } else {
3045
- return ParseStatus.mergeArray(status, items);
3045
+ return ParseStatus.mergeArray(status2, items);
3046
3046
  }
3047
3047
  }
3048
3048
  get items() {
@@ -3074,7 +3074,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
3074
3074
  return this._def.valueType;
3075
3075
  }
3076
3076
  _parse(input2) {
3077
- const { status, ctx } = this._processInputParams(input2);
3077
+ const { status: status2, ctx } = this._processInputParams(input2);
3078
3078
  if (ctx.parsedType !== ZodParsedType.object) {
3079
3079
  addIssueToContext(ctx, {
3080
3080
  code: ZodIssueCode.invalid_type,
@@ -3094,9 +3094,9 @@ var ZodRecord = class _ZodRecord extends ZodType {
3094
3094
  });
3095
3095
  }
3096
3096
  if (ctx.common.async) {
3097
- return ParseStatus.mergeObjectAsync(status, pairs);
3097
+ return ParseStatus.mergeObjectAsync(status2, pairs);
3098
3098
  } else {
3099
- return ParseStatus.mergeObjectSync(status, pairs);
3099
+ return ParseStatus.mergeObjectSync(status2, pairs);
3100
3100
  }
3101
3101
  }
3102
3102
  get element() {
@@ -3127,7 +3127,7 @@ var ZodMap = class extends ZodType {
3127
3127
  return this._def.valueType;
3128
3128
  }
3129
3129
  _parse(input2) {
3130
- const { status, ctx } = this._processInputParams(input2);
3130
+ const { status: status2, ctx } = this._processInputParams(input2);
3131
3131
  if (ctx.parsedType !== ZodParsedType.map) {
3132
3132
  addIssueToContext(ctx, {
3133
3133
  code: ZodIssueCode.invalid_type,
@@ -3154,11 +3154,11 @@ var ZodMap = class extends ZodType {
3154
3154
  return INVALID;
3155
3155
  }
3156
3156
  if (key.status === "dirty" || value.status === "dirty") {
3157
- status.dirty();
3157
+ status2.dirty();
3158
3158
  }
3159
3159
  finalMap.set(key.value, value.value);
3160
3160
  }
3161
- return { status: status.value, value: finalMap };
3161
+ return { status: status2.value, value: finalMap };
3162
3162
  });
3163
3163
  } else {
3164
3164
  const finalMap = /* @__PURE__ */ new Map();
@@ -3169,11 +3169,11 @@ var ZodMap = class extends ZodType {
3169
3169
  return INVALID;
3170
3170
  }
3171
3171
  if (key.status === "dirty" || value.status === "dirty") {
3172
- status.dirty();
3172
+ status2.dirty();
3173
3173
  }
3174
3174
  finalMap.set(key.value, value.value);
3175
3175
  }
3176
- return { status: status.value, value: finalMap };
3176
+ return { status: status2.value, value: finalMap };
3177
3177
  }
3178
3178
  }
3179
3179
  };
@@ -3187,7 +3187,7 @@ ZodMap.create = (keyType, valueType, params) => {
3187
3187
  };
3188
3188
  var ZodSet = class _ZodSet extends ZodType {
3189
3189
  _parse(input2) {
3190
- const { status, ctx } = this._processInputParams(input2);
3190
+ const { status: status2, ctx } = this._processInputParams(input2);
3191
3191
  if (ctx.parsedType !== ZodParsedType.set) {
3192
3192
  addIssueToContext(ctx, {
3193
3193
  code: ZodIssueCode.invalid_type,
@@ -3207,7 +3207,7 @@ var ZodSet = class _ZodSet extends ZodType {
3207
3207
  exact: false,
3208
3208
  message: def.minSize.message
3209
3209
  });
3210
- status.dirty();
3210
+ status2.dirty();
3211
3211
  }
3212
3212
  }
3213
3213
  if (def.maxSize !== null) {
@@ -3220,7 +3220,7 @@ var ZodSet = class _ZodSet extends ZodType {
3220
3220
  exact: false,
3221
3221
  message: def.maxSize.message
3222
3222
  });
3223
- status.dirty();
3223
+ status2.dirty();
3224
3224
  }
3225
3225
  }
3226
3226
  const valueType = this._def.valueType;
@@ -3230,10 +3230,10 @@ var ZodSet = class _ZodSet extends ZodType {
3230
3230
  if (element.status === "aborted")
3231
3231
  return INVALID;
3232
3232
  if (element.status === "dirty")
3233
- status.dirty();
3233
+ status2.dirty();
3234
3234
  parsedSet.add(element.value);
3235
3235
  }
3236
- return { status: status.value, value: parsedSet };
3236
+ return { status: status2.value, value: parsedSet };
3237
3237
  }
3238
3238
  const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));
3239
3239
  if (ctx.common.async) {
@@ -3564,15 +3564,15 @@ var ZodEffects = class extends ZodType {
3564
3564
  return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema;
3565
3565
  }
3566
3566
  _parse(input2) {
3567
- const { status, ctx } = this._processInputParams(input2);
3567
+ const { status: status2, ctx } = this._processInputParams(input2);
3568
3568
  const effect = this._def.effect || null;
3569
3569
  const checkCtx = {
3570
3570
  addIssue: (arg) => {
3571
3571
  addIssueToContext(ctx, arg);
3572
3572
  if (arg.fatal) {
3573
- status.abort();
3573
+ status2.abort();
3574
3574
  } else {
3575
- status.dirty();
3575
+ status2.dirty();
3576
3576
  }
3577
3577
  },
3578
3578
  get path() {
@@ -3584,7 +3584,7 @@ var ZodEffects = class extends ZodType {
3584
3584
  const processed = effect.transform(ctx.data, checkCtx);
3585
3585
  if (ctx.common.async) {
3586
3586
  return Promise.resolve(processed).then(async (processed2) => {
3587
- if (status.value === "aborted")
3587
+ if (status2.value === "aborted")
3588
3588
  return INVALID;
3589
3589
  const result = await this._def.schema._parseAsync({
3590
3590
  data: processed2,
@@ -3595,12 +3595,12 @@ var ZodEffects = class extends ZodType {
3595
3595
  return INVALID;
3596
3596
  if (result.status === "dirty")
3597
3597
  return DIRTY(result.value);
3598
- if (status.value === "dirty")
3598
+ if (status2.value === "dirty")
3599
3599
  return DIRTY(result.value);
3600
3600
  return result;
3601
3601
  });
3602
3602
  } else {
3603
- if (status.value === "aborted")
3603
+ if (status2.value === "aborted")
3604
3604
  return INVALID;
3605
3605
  const result = this._def.schema._parseSync({
3606
3606
  data: processed,
@@ -3611,7 +3611,7 @@ var ZodEffects = class extends ZodType {
3611
3611
  return INVALID;
3612
3612
  if (result.status === "dirty")
3613
3613
  return DIRTY(result.value);
3614
- if (status.value === "dirty")
3614
+ if (status2.value === "dirty")
3615
3615
  return DIRTY(result.value);
3616
3616
  return result;
3617
3617
  }
@@ -3636,17 +3636,17 @@ var ZodEffects = class extends ZodType {
3636
3636
  if (inner.status === "aborted")
3637
3637
  return INVALID;
3638
3638
  if (inner.status === "dirty")
3639
- status.dirty();
3639
+ status2.dirty();
3640
3640
  executeRefinement(inner.value);
3641
- return { status: status.value, value: inner.value };
3641
+ return { status: status2.value, value: inner.value };
3642
3642
  } else {
3643
3643
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {
3644
3644
  if (inner.status === "aborted")
3645
3645
  return INVALID;
3646
3646
  if (inner.status === "dirty")
3647
- status.dirty();
3647
+ status2.dirty();
3648
3648
  return executeRefinement(inner.value).then(() => {
3649
- return { status: status.value, value: inner.value };
3649
+ return { status: status2.value, value: inner.value };
3650
3650
  });
3651
3651
  });
3652
3652
  }
@@ -3664,13 +3664,13 @@ var ZodEffects = class extends ZodType {
3664
3664
  if (result instanceof Promise) {
3665
3665
  throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
3666
3666
  }
3667
- return { status: status.value, value: result };
3667
+ return { status: status2.value, value: result };
3668
3668
  } else {
3669
3669
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
3670
3670
  if (!isValid(base))
3671
3671
  return INVALID;
3672
3672
  return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
3673
- status: status.value,
3673
+ status: status2.value,
3674
3674
  value: result
3675
3675
  }));
3676
3676
  });
@@ -3849,7 +3849,7 @@ var ZodBranded = class extends ZodType {
3849
3849
  };
3850
3850
  var ZodPipeline = class _ZodPipeline extends ZodType {
3851
3851
  _parse(input2) {
3852
- const { status, ctx } = this._processInputParams(input2);
3852
+ const { status: status2, ctx } = this._processInputParams(input2);
3853
3853
  if (ctx.common.async) {
3854
3854
  const handleAsync = async () => {
3855
3855
  const inResult = await this._def.in._parseAsync({
@@ -3860,7 +3860,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
3860
3860
  if (inResult.status === "aborted")
3861
3861
  return INVALID;
3862
3862
  if (inResult.status === "dirty") {
3863
- status.dirty();
3863
+ status2.dirty();
3864
3864
  return DIRTY(inResult.value);
3865
3865
  } else {
3866
3866
  return this._def.out._parseAsync({
@@ -3880,7 +3880,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
3880
3880
  if (inResult.status === "aborted")
3881
3881
  return INVALID;
3882
3882
  if (inResult.status === "dirty") {
3883
- status.dirty();
3883
+ status2.dirty();
3884
3884
  return {
3885
3885
  status: "dirty",
3886
3886
  value: inResult.value
@@ -4631,6 +4631,39 @@ function projectCodexThread(value) {
4631
4631
  ...typeof value.isPinned === "boolean" ? { pinned: value.isPinned } : {}
4632
4632
  };
4633
4633
  }
4634
+ function deriveCodexCwdWorkspaces(values) {
4635
+ const byPath = /* @__PURE__ */ new Map();
4636
+ const usedIds = /* @__PURE__ */ new Set();
4637
+ for (const value of values) {
4638
+ const session = projectCodexThread(value);
4639
+ if (session === void 0 || session.cwd === void 0 || session.cwd.length === 0)
4640
+ continue;
4641
+ const path = session.cwd;
4642
+ if (!isAbsoluteWorkspacePath(path))
4643
+ continue;
4644
+ const key = normalizeWorkspacePath(path);
4645
+ const existing = byPath.get(key);
4646
+ if (existing !== void 0) {
4647
+ existing.createdAt = earliestTimestamp(existing.createdAt, session.createdAt);
4648
+ existing.updatedAt = Math.max(existing.updatedAt, session.updatedAt);
4649
+ continue;
4650
+ }
4651
+ const baseId = `cwd-${hashWorkspacePath(key)}`;
4652
+ let id2 = baseId;
4653
+ for (let suffix = 2; usedIds.has(id2); suffix += 1)
4654
+ id2 = `${baseId}-${suffix}`;
4655
+ usedIds.add(id2);
4656
+ byPath.set(key, {
4657
+ id: id2,
4658
+ name: workspaceBasename(path),
4659
+ path,
4660
+ position: byPath.size,
4661
+ createdAt: session.createdAt,
4662
+ updatedAt: session.updatedAt
4663
+ });
4664
+ }
4665
+ return [...byPath.values()];
4666
+ }
4634
4667
  function projectCodexHistory(thread) {
4635
4668
  if (!isRecord(thread) || typeof thread.id !== "string" || !Array.isArray(thread.turns))
4636
4669
  return [];
@@ -4884,6 +4917,31 @@ function boundedText(value) {
4884
4917
  return value.length <= MAX_DISPLAY_ITEM_TEXT ? value : `${value.slice(0, MAX_DISPLAY_ITEM_TEXT)}
4885
4918
  \u2026`;
4886
4919
  }
4920
+ function normalizeWorkspacePath(value) {
4921
+ return value.replace(/[\\/]+$/u, "") || value;
4922
+ }
4923
+ function isAbsoluteWorkspacePath(value) {
4924
+ return value.startsWith("/") || /^\\\\[^\\]+\\[^\\]+/u.test(value) || /^[A-Za-z]:[\\/]/u.test(value);
4925
+ }
4926
+ function workspaceBasename(value) {
4927
+ const normalized = normalizeWorkspacePath(value);
4928
+ return normalized.split(/[\\/]/u).at(-1) || value;
4929
+ }
4930
+ function earliestTimestamp(left, right) {
4931
+ if (left <= 0)
4932
+ return right;
4933
+ if (right <= 0)
4934
+ return left;
4935
+ return Math.min(left, right);
4936
+ }
4937
+ function hashWorkspacePath(value) {
4938
+ let hash = 2166136261;
4939
+ for (let index = 0; index < value.length; index += 1) {
4940
+ hash ^= value.charCodeAt(index);
4941
+ hash = Math.imul(hash, 16777619);
4942
+ }
4943
+ return (hash >>> 0).toString(36);
4944
+ }
4887
4945
  function normalizeTimestamp(value) {
4888
4946
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
4889
4947
  return 0;
@@ -4976,13 +5034,13 @@ var RemoteClientCore = class {
4976
5034
  if (signal?.aborted)
4977
5035
  throw rpcAbortedError(method, signal.reason);
4978
5036
  const request = createRpcRequest(method, params);
4979
- const result = new Promise((resolve2, reject) => {
5037
+ const result = new Promise((resolve3, reject) => {
4980
5038
  const timer = setTimeout(() => {
4981
5039
  this.rejectPending(request.id, new RemoteClientError("RPC_TIMEOUT", `RPC ${method} timed out after ${this.timeoutMs}ms`));
4982
5040
  }, this.timeoutMs);
4983
5041
  const pending = {
4984
5042
  method,
4985
- resolve: resolve2,
5043
+ resolve: resolve3,
4986
5044
  reject,
4987
5045
  timer
4988
5046
  };
@@ -5546,8 +5604,8 @@ var RtcDataChannelTransport = class {
5546
5604
  if (this.connectPromise !== void 0)
5547
5605
  return this.connectPromise;
5548
5606
  this.armAbort(signal);
5549
- this.connectPromise = new Promise((resolve2, reject) => {
5550
- this.openResolve = resolve2;
5607
+ this.connectPromise = new Promise((resolve3, reject) => {
5608
+ this.openResolve = resolve3;
5551
5609
  this.openReject = reject;
5552
5610
  this.negotiateTimer = setTimeout(() => {
5553
5611
  void this.failOpenAfterStats(new RtcConnectError("RTC_CONNECT_TIMEOUT", `WebRTC negotiation timed out after ${this.negotiateTimeoutMs}ms.`));
@@ -5762,9 +5820,9 @@ var RtcDataChannelTransport = class {
5762
5820
  if (this.closed || this.opened)
5763
5821
  return;
5764
5822
  this.opened = true;
5765
- const resolve2 = this.openResolve;
5823
+ const resolve3 = this.openResolve;
5766
5824
  this.clearNegotiation();
5767
- void this.resolveSelectedTransport().then(() => resolve2?.());
5825
+ void this.resolveSelectedTransport().then(() => resolve3?.());
5768
5826
  };
5769
5827
  channel.onmessage = (event) => {
5770
5828
  if (this.closed || !this.opened)
@@ -6158,7 +6216,7 @@ function asError(error) {
6158
6216
  return error instanceof Error ? error : new RtcConnectError("RTC_FAILED", "WebRTC negotiation failed.");
6159
6217
  }
6160
6218
  function sleep(ms) {
6161
- return new Promise((resolve2) => setTimeout(resolve2, ms));
6219
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
6162
6220
  }
6163
6221
 
6164
6222
  // ../webrtc/dist/adaptive-transport.js
@@ -6196,8 +6254,8 @@ var AdaptiveTransport = class extends BaseTransport {
6196
6254
  this.socket = new WebSocket(this.url);
6197
6255
  this.controlFrameLimits = {};
6198
6256
  this.socket.binaryType = "arraybuffer";
6199
- await new Promise((resolve2, reject) => {
6200
- this.readyResolve = resolve2;
6257
+ await new Promise((resolve3, reject) => {
6258
+ this.readyResolve = resolve3;
6201
6259
  this.readyReject = reject;
6202
6260
  this.handshakeTimer = setTimeout(() => this.failConnection(new Error("Adaptive control handshake timed out")), this.options.handshakeTimeoutMs ?? 15e3);
6203
6261
  const socket = this.socket;
@@ -13774,7 +13832,7 @@ var ClientSecureTransport = class {
13774
13832
  }
13775
13833
  };
13776
13834
  async function waitForResponder(inner, noise) {
13777
- await new Promise((resolve2, reject) => {
13835
+ await new Promise((resolve3, reject) => {
13778
13836
  let settled = false;
13779
13837
  const timer = setTimeout(() => finish(new Error("Noise IK handshake timed out.")), 1e4);
13780
13838
  const unsubscribe = inner.onHandshake((step, data) => {
@@ -13793,7 +13851,7 @@ async function waitForResponder(inner, noise) {
13793
13851
  settled = true;
13794
13852
  clearTimeout(timer);
13795
13853
  unsubscribe();
13796
- if (error === void 0) resolve2();
13854
+ if (error === void 0) resolve3();
13797
13855
  else reject(error);
13798
13856
  };
13799
13857
  void inner.sendHandshake(1, noise.writeHandshake()).catch((error) => {
@@ -14051,7 +14109,7 @@ var AsyncFrameQueue = class {
14051
14109
  continue;
14052
14110
  }
14053
14111
  if (this.closed) return;
14054
- const next = await new Promise((resolve2) => this.waiters.push(resolve2));
14112
+ const next = await new Promise((resolve3) => this.waiters.push(resolve3));
14055
14113
  if (next.done) return;
14056
14114
  yield next.value;
14057
14115
  }
@@ -14239,7 +14297,7 @@ var AsyncValueQueue = class {
14239
14297
  if (this.error !== void 0) throw this.error;
14240
14298
  return;
14241
14299
  }
14242
- const next = await new Promise((resolve2) => this.waiters.push(resolve2));
14300
+ const next = await new Promise((resolve3) => this.waiters.push(resolve3));
14243
14301
  if (next.done) {
14244
14302
  if (this.error !== void 0) throw this.error;
14245
14303
  return;
@@ -14479,7 +14537,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14479
14537
  case "commands/list":
14480
14538
  return ok(this.commandList(args));
14481
14539
  case "commands/execute":
14482
- return ok(this.executeCommand(args));
14540
+ return ok(await this.executeCommand(args, signal));
14483
14541
  default:
14484
14542
  return fail("method-not-found", `CodeX virtual Harness does not implement ${endpoint}.`);
14485
14543
  }
@@ -14553,6 +14611,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14553
14611
  permissionSelection(sessionId) {
14554
14612
  return this.selectedPermissions.get(sessionId) ?? CODEX_DEFAULT_PERMISSION;
14555
14613
  }
14614
+ setPermissionSelection(sessionId, preset) {
14615
+ this.selectedPermissions.set(sessionId, preset);
14616
+ this.publishProjection(sessionId, "permissions", codexPermissionsProjection(preset), this.nextProjectionSeq());
14617
+ }
14556
14618
  commandList(args) {
14557
14619
  nativeThreadId(requiredString(args.agentId, "agentId"));
14558
14620
  return [{
@@ -14561,9 +14623,9 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14561
14623
  input: { hint: "<preset>" }
14562
14624
  }];
14563
14625
  }
14564
- executeCommand(args) {
14626
+ async executeCommand(args, signal) {
14565
14627
  const sessionId = requiredString(args.agentId, "agentId");
14566
- nativeThreadId(sessionId);
14628
+ const threadId = nativeThreadId(sessionId);
14567
14629
  const line = requiredString(args.line, "command line").trim();
14568
14630
  if (!Array.isArray(args.images)) throw new Error("The CodeX command image list is required.");
14569
14631
  const match = /^\/permission(?:\s+([\s\S]*))?$/u.exec(line);
@@ -14582,9 +14644,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14582
14644
  commandId,
14583
14645
  result: { kind: "error", text: `unknown preset "${requested}" (available: workspace-write, danger-full-access)` }
14584
14646
  };
14585
- this.selectedPermissions.set(sessionId, requested);
14586
- this.publishProjection(sessionId, "permissions", codexPermissionsProjection(requested), this.nextProjectionSeq());
14587
- return { commandId, result: { kind: "success", text: `preset ${requested}` } };
14647
+ const result = await this.client.request("thread/resume", { threadId, permissionPreset: requested }, signal);
14648
+ const effective = codexPermissionPresetFromResponse(result) ?? requested;
14649
+ this.setPermissionSelection(sessionId, effective);
14650
+ return { commandId, result: { kind: "success", text: `preset ${effective}` } };
14588
14651
  }
14589
14652
  sessionTitle(sessionId) {
14590
14653
  const session = this.catalog?.sessions.find((item) => item.id === sessionId);
@@ -14728,7 +14791,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14728
14791
  queue,
14729
14792
  nextSeq: history.cursor + 1,
14730
14793
  turn: history.nextTurn,
14731
- stepOpen: false,
14794
+ stepOpen: history.activeTurnId !== void 0,
14732
14795
  startedItems: /* @__PURE__ */ new Set(),
14733
14796
  completedItems: /* @__PURE__ */ new Set(),
14734
14797
  streamedBlocks: /* @__PURE__ */ new Map(),
@@ -14737,7 +14800,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14737
14800
  liveItems: /* @__PURE__ */ new Map(),
14738
14801
  liveToolOutput: /* @__PURE__ */ new Map(),
14739
14802
  liveToolResultSeq: /* @__PURE__ */ new Map(),
14740
- requestId: this.pendingRequestIds.get(sessionId)
14803
+ requestId: this.pendingRequestIds.get(sessionId),
14804
+ ...history.activeTurnId === void 0 ? {} : { activeTurnId: history.activeTurnId }
14741
14805
  };
14742
14806
  this.follows.add(follow);
14743
14807
  queue.push({
@@ -14786,6 +14850,11 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14786
14850
  this.publishProjection(follow.sessionId, "title", title, seq);
14787
14851
  return;
14788
14852
  }
14853
+ if (frame.method === "thread/settings/updated") {
14854
+ const preset = codexPermissionPresetFromResponse(params);
14855
+ if (preset !== void 0) this.setPermissionSelection(follow.sessionId, preset);
14856
+ return;
14857
+ }
14789
14858
  if (frame.method === "turn/started") {
14790
14859
  const turn = record(params.turn);
14791
14860
  this.resetLiveTurn(follow);
@@ -14840,8 +14909,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14840
14909
  const item = record(value);
14841
14910
  const content = string(item.step);
14842
14911
  if (content === void 0) return [];
14843
- const status = item.status === "inProgress" ? "in_progress" : item.status === "completed" ? "completed" : "pending";
14844
- return [{ content, status }];
14912
+ const status2 = item.status === "inProgress" ? "in_progress" : item.status === "completed" ? "completed" : "pending";
14913
+ return [{ content, status: status2 }];
14845
14914
  });
14846
14915
  this.pushEvent(follow, "todo/write", { todos });
14847
14916
  return;
@@ -14871,10 +14940,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14871
14940
  return;
14872
14941
  }
14873
14942
  if (frame.method === "thread/status/changed") {
14874
- const status = record(params.status);
14875
- const running = status.type === "active";
14943
+ const status2 = record(params.status);
14944
+ const running = status2.type === "active";
14876
14945
  this.emitRemoteEvent("api-session/status", [follow.sessionId, running]);
14877
- if (status.type === "systemError") {
14946
+ if (status2.type === "systemError") {
14878
14947
  this.broadcastRcHost({
14879
14948
  type: "host/agent-error",
14880
14949
  sessionId: follow.sessionId,
@@ -15168,12 +15237,13 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15168
15237
  if (cwd === void 0) return failure("workspace-not-found", "The CodeX virtual Workspace was not found.");
15169
15238
  const directory = await this.models(signal);
15170
15239
  const selection = directory.default;
15171
- const permissionPreset2 = CODEX_DEFAULT_PERMISSION;
15240
+ const requestedPreset = CODEX_DEFAULT_PERMISSION;
15172
15241
  const result = record(await this.client.request("thread/start", {
15173
15242
  cwd,
15174
15243
  model: selection.model,
15175
- permissionPreset: permissionPreset2
15244
+ permissionPreset: requestedPreset
15176
15245
  }, signal));
15246
+ const permissionPreset2 = codexPermissionPresetFromResponse(result) ?? requestedPreset;
15177
15247
  const thread = { ...record(result.thread), cwd, turns: array(record(result.thread).turns) };
15178
15248
  const projected = projectCodexThread(thread);
15179
15249
  if (projected === void 0) return failure("internal", "CodeX returned an invalid Thread.");
@@ -15194,11 +15264,12 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15194
15264
  }
15195
15265
  async forkSession(request, signal) {
15196
15266
  const sessionId = requiredString(request.sessionId, "sessionId");
15197
- const permissionPreset2 = this.permissionSelection(sessionId);
15267
+ let permissionPreset2 = this.selectedPermissions.get(sessionId);
15198
15268
  const result = record(await this.client.request("thread/fork", {
15199
15269
  threadId: nativeThreadId(sessionId),
15200
- permissionPreset: permissionPreset2
15270
+ ...permissionPreset2 === void 0 ? {} : { permissionPreset: permissionPreset2 }
15201
15271
  }, signal));
15272
+ permissionPreset2 = codexPermissionPresetFromResponse(result) ?? permissionPreset2 ?? CODEX_DEFAULT_PERMISSION;
15202
15273
  const projected = projectCodexThread(record(result.thread));
15203
15274
  if (projected === void 0) return failure("internal", "CodeX returned an invalid forked Thread.");
15204
15275
  this.selectedModels.set(projected.id, this.modelSelection(sessionId, await this.models(signal)));
@@ -15215,14 +15286,20 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15215
15286
  if (input2 === void 0) return failure("attachment-error", "CodeX virtual Sessions accept text and pasted PNG, JPEG, WebP, or GIF images only.");
15216
15287
  const threadId = nativeThreadId(sessionId);
15217
15288
  const selection = this.modelSelection(sessionId, await this.models(signal));
15218
- const permissionPreset2 = this.permissionSelection(sessionId);
15289
+ let permissionPreset2 = this.selectedPermissions.get(sessionId);
15219
15290
  const isFreshBlankThread = this.blankThreads.has(threadId) || this.pendingThreads.has(threadId);
15220
15291
  await this.ensureRcFollow(sessionId);
15221
- if (!isFreshBlankThread) await this.client.request("thread/resume", {
15222
- threadId,
15223
- model: selection.model,
15224
- permissionPreset: permissionPreset2
15225
- }, signal);
15292
+ if (!isFreshBlankThread) {
15293
+ const resumeResult = await this.client.request("thread/resume", {
15294
+ threadId,
15295
+ model: selection.model
15296
+ }, signal);
15297
+ const serverPreset = codexPermissionPresetFromResponse(resumeResult);
15298
+ if (permissionPreset2 === void 0 && serverPreset !== void 0) {
15299
+ permissionPreset2 = serverPreset;
15300
+ this.setPermissionSelection(sessionId, serverPreset);
15301
+ }
15302
+ }
15226
15303
  this.pendingRequestIds.set(sessionId, string(request.requestId) ?? "");
15227
15304
  const mode = request.mode === "steer" ? "turn/steer" : "turn/start";
15228
15305
  if (mode === "turn/steer") {
@@ -15234,7 +15311,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15234
15311
  threadId,
15235
15312
  input: input2,
15236
15313
  ...codexModelParams(selection),
15237
- permissionPreset: permissionPreset2
15314
+ ...permissionPreset2 === void 0 ? {} : { permissionPreset: permissionPreset2 }
15238
15315
  }, signal);
15239
15316
  }
15240
15317
  this.blankThreads.delete(threadId);
@@ -15376,7 +15453,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15376
15453
  queue: new AsyncValueQueue2(controller.signal),
15377
15454
  nextSeq: history.cursor + 1,
15378
15455
  turn: history.nextTurn,
15379
- stepOpen: false,
15456
+ stepOpen: history.activeTurnId !== void 0,
15380
15457
  startedItems: /* @__PURE__ */ new Set(),
15381
15458
  completedItems: /* @__PURE__ */ new Set(),
15382
15459
  streamedBlocks: /* @__PURE__ */ new Map(),
@@ -15386,7 +15463,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15386
15463
  liveToolOutput: /* @__PURE__ */ new Map(),
15387
15464
  liveToolResultSeq: /* @__PURE__ */ new Map(),
15388
15465
  requestId: this.pendingRequestIds.get(sessionId),
15389
- rcOnly: true
15466
+ rcOnly: true,
15467
+ ...history.activeTurnId === void 0 ? {} : { activeTurnId: history.activeTurnId }
15390
15468
  };
15391
15469
  this.follows.add(follow);
15392
15470
  try {
@@ -15520,7 +15598,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15520
15598
  }
15521
15599
  };
15522
15600
  async function loadCatalog(client, signal, pendingThreads) {
15523
- const projects = await loadCodexProjects(client, signal);
15601
+ let projects = await loadCodexProjects(client, signal);
15524
15602
  const threads = [];
15525
15603
  let cursor2;
15526
15604
  for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
@@ -15545,6 +15623,16 @@ async function loadCatalog(client, signal, pendingThreads) {
15545
15623
  else threads.unshift(thread);
15546
15624
  }
15547
15625
  }
15626
+ if (projects.length === 0) {
15627
+ projects = deriveCodexCwdWorkspaces(threads).map((workspace) => ({
15628
+ id: workspace.id,
15629
+ name: workspace.name,
15630
+ roots: [{ path: workspace.path }],
15631
+ position: workspace.position,
15632
+ createdAt: workspace.createdAt,
15633
+ updatedAt: workspace.updatedAt
15634
+ }));
15635
+ }
15548
15636
  const projected = threads.map((thread) => {
15549
15637
  const session = projectCodexThread(thread);
15550
15638
  return session === void 0 ? void 0 : { thread, session };
@@ -15653,6 +15741,7 @@ function projectCodexNativeHistory(thread, sessionId) {
15653
15741
  const entries = [];
15654
15742
  let seq = 0;
15655
15743
  let turnNumber = 0;
15744
+ const active = activeTurnId(thread.turns);
15656
15745
  const append = (type, data, time = Date.now(), view, sourceEventSeqs) => {
15657
15746
  const eventSeq = seq++;
15658
15747
  entries.push({
@@ -15672,6 +15761,7 @@ function projectCodexNativeHistory(thread, sessionId) {
15672
15761
  for (const rawTurn of array(thread.turns)) {
15673
15762
  const turn = record(rawTurn);
15674
15763
  turnNumber += 1;
15764
+ const turnActive = isActiveCodexTurn(turn);
15675
15765
  const time = normalizeTime(turn.createdAt) || normalizeTime(turn.startedAt) || normalizeTime(thread.createdAt) || Date.now();
15676
15766
  append("turn/start", { turn: turnNumber }, time);
15677
15767
  append("step/start", { turn: turnNumber, step: 1 }, time);
@@ -15689,6 +15779,7 @@ function projectCodexNativeHistory(thread, sessionId) {
15689
15779
  if (event.type === "tool/call") toolCallSeq = eventSeq;
15690
15780
  }
15691
15781
  }
15782
+ if (turnActive) continue;
15692
15783
  append("step/end", { turn: turnNumber, step: 1 }, normalizeTime(turn.updatedAt) || normalizeTime(turn.completedAt) || time);
15693
15784
  append("turn/end", {
15694
15785
  turn: turnNumber,
@@ -15705,7 +15796,8 @@ function projectCodexNativeHistory(thread, sessionId) {
15705
15796
  },
15706
15797
  entries,
15707
15798
  lastSeq: seq - 1,
15708
- nextTurn: turnNumber
15799
+ nextTurn: turnNumber,
15800
+ ...active === void 0 ? {} : { activeTurnId: active }
15709
15801
  };
15710
15802
  }
15711
15803
  function paginateCodexNativeHistory(history, request) {
@@ -15729,7 +15821,8 @@ function paginateCodexNativeHistory(history, request) {
15729
15821
  cursor: history.lastSeq,
15730
15822
  nextTurn: history.nextTurn,
15731
15823
  records: window.filter((entry) => entry.event.seq >= cut),
15732
- hasMore: window.some((entry) => entry.event.seq < cut)
15824
+ hasMore: window.some((entry) => entry.event.seq < cut),
15825
+ ...history.activeTurnId === void 0 ? {} : { activeTurnId: history.activeTurnId }
15733
15826
  };
15734
15827
  }
15735
15828
  function itemEvents(item, turn, step, requestId, selection = modelSelection(), sessionId = CODEX_SESSION_PREFIX) {
@@ -16242,6 +16335,22 @@ function codexImageLimitsProjection() {
16242
16335
  function isCodexPermissionPreset(value) {
16243
16336
  return value === "workspace-write" || value === "danger-full-access";
16244
16337
  }
16338
+ function codexPermissionPresetFromResponse(value) {
16339
+ const root = record(value);
16340
+ return codexPermissionPresetFromPolicyRecord(root) ?? codexPermissionPresetFromPolicyRecord(record(root.threadSettings));
16341
+ }
16342
+ function codexPermissionPresetFromPolicyRecord(value) {
16343
+ const sandbox = value.sandbox ?? value.sandboxPolicy;
16344
+ if (value.approvalPolicy === "never" && isDangerFullAccessSandbox(sandbox)) return "danger-full-access";
16345
+ if (value.approvalPolicy === "on-request" && isWorkspaceWriteSandbox(sandbox)) return "workspace-write";
16346
+ return void 0;
16347
+ }
16348
+ function isDangerFullAccessSandbox(value) {
16349
+ return value === "danger-full-access" || record(value).type === "dangerFullAccess";
16350
+ }
16351
+ function isWorkspaceWriteSandbox(value) {
16352
+ return value === "workspace-write" || record(value).type === "workspaceWrite";
16353
+ }
16245
16354
  function modelCatalog(directory) {
16246
16355
  return {
16247
16356
  default: directory.default,
@@ -16382,6 +16491,17 @@ function nativeThreadId(sessionId) {
16382
16491
  }
16383
16492
  return sessionId.slice(CODEX_SESSION_PREFIX.length);
16384
16493
  }
16494
+ function activeTurnId(value) {
16495
+ if (!Array.isArray(value)) return void 0;
16496
+ for (let index = value.length - 1; index >= 0; index -= 1) {
16497
+ const turn = record(value[index]);
16498
+ if (isActiveCodexTurn(turn) && typeof turn.id === "string" && turn.id.length > 0) return turn.id;
16499
+ }
16500
+ return void 0;
16501
+ }
16502
+ function isActiveCodexTurn(turn) {
16503
+ return turn.status === "inProgress" || turn.status === "running";
16504
+ }
16385
16505
  function carrierArgs(payload) {
16386
16506
  return record(record(payload).args);
16387
16507
  }
@@ -16464,7 +16584,7 @@ function optionalPositiveInteger(value) {
16464
16584
  function isCodexNativeHistoryPage(value, sessionId) {
16465
16585
  const page = record(value);
16466
16586
  const header = record(page.header);
16467
- return header.id === sessionId && integer(page.cursor) !== void 0 && integer(page.nextTurn) !== void 0 && Array.isArray(page.records) && typeof page.hasMore === "boolean";
16587
+ return header.id === sessionId && integer(page.cursor) !== void 0 && integer(page.nextTurn) !== void 0 && Array.isArray(page.records) && typeof page.hasMore === "boolean" && (page.activeTurnId === void 0 || typeof page.activeTurnId === "string");
16468
16588
  }
16469
16589
  function truncateCodePoints(value, maximum) {
16470
16590
  return [...value].slice(0, maximum).join("");
@@ -16541,7 +16661,7 @@ var AsyncValueQueue2 = class {
16541
16661
  continue;
16542
16662
  }
16543
16663
  if (this.closed) return;
16544
- const next = await new Promise((resolve2) => this.waiters.push(resolve2));
16664
+ const next = await new Promise((resolve3) => this.waiters.push(resolve3));
16545
16665
  if (next.done) return;
16546
16666
  yield next.value;
16547
16667
  }
@@ -16554,6 +16674,7 @@ import { platform } from "node:os";
16554
16674
  // src/config.ts
16555
16675
  import { hostname } from "node:os";
16556
16676
  import s from "@deepseek-ai/schemastery";
16677
+ var DEFAULT_REMOTE_SERVER_URL = "https://dsh.r2049.cn";
16557
16678
  var Config = s.object({
16558
16679
  enabled: s.boolean(),
16559
16680
  role: s.union(["host", "client", "both"]),
@@ -16643,9 +16764,10 @@ function normalizeServerUrl(value) {
16643
16764
  }
16644
16765
 
16645
16766
  // src/version.ts
16646
- var PLUGIN_VERSION = "0.4.3";
16767
+ var PLUGIN_VERSION = "0.4.4";
16647
16768
 
16648
16769
  // src/server-api.ts
16770
+ var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
16649
16771
  var HostServerApi = class {
16650
16772
  constructor(serverUrl, store, fetchImplementation = fetch, role = "host") {
16651
16773
  this.store = store;
@@ -16694,20 +16816,20 @@ var HostServerApi = class {
16694
16816
  if (account.length === 0 || password.length === 0) {
16695
16817
  throw new ServerApiError("INVALID_MESSAGE", "Email and password are required.", false);
16696
16818
  }
16697
- const login = validateWebLogin(await this.publicRequest("/api/v1/auth/login", {
16819
+ const login2 = validateWebLogin(await this.publicRequest("/api/v1/auth/login", {
16698
16820
  method: "POST",
16699
16821
  body: JSON.stringify({ email: account, password })
16700
16822
  }));
16701
16823
  await this.register(identity, {
16702
- accountToken: login.token,
16703
- account: login.account,
16824
+ accountToken: login2.token,
16825
+ account: login2.account,
16704
16826
  authorizationMethod: "account"
16705
16827
  });
16706
16828
  return {
16707
16829
  method: "account",
16708
- account: login.account,
16709
- expiresAt: login.expiresAt,
16710
- isAdmin: login.isAdmin
16830
+ account: login2.account,
16831
+ expiresAt: login2.expiresAt,
16832
+ isAdmin: login2.isAdmin
16711
16833
  };
16712
16834
  }
16713
16835
  async startOAuthQrLogin(provider = "zhihu") {
@@ -16715,10 +16837,11 @@ var HostServerApi = class {
16715
16837
  method: "POST",
16716
16838
  body: "{}"
16717
16839
  }), "QR login");
16718
- if (typeof value.qrId !== "string" || value.qrId.length < 20 || typeof value.scanUrl !== "string" || !value.scanUrl.startsWith(`${this.baseUrl}/`) || !Number.isSafeInteger(value.expiresIn) || provider === "github" && value.provider !== "github" || value.provider !== void 0 && value.provider !== provider) {
16840
+ const scanUrl = normalizeOAuthScanUrl(value.scanUrl, this.baseUrl);
16841
+ if (typeof value.qrId !== "string" || value.qrId.length < 20 || scanUrl === void 0 || !Number.isSafeInteger(value.expiresIn) || provider === "github" && value.provider !== "github" || value.provider !== void 0 && value.provider !== provider) {
16719
16842
  throw new ServerApiError("INVALID_MESSAGE", "The Server returned an invalid QR login session.", false);
16720
16843
  }
16721
- return { qrId: value.qrId, scanUrl: value.scanUrl, expiresIn: value.expiresIn };
16844
+ return { qrId: value.qrId, scanUrl, expiresIn: value.expiresIn };
16722
16845
  }
16723
16846
  async pollOAuthQrLogin(identity, qrId, recoverIdentity) {
16724
16847
  this.bindIdentity(identity);
@@ -16933,17 +17056,26 @@ var HostServerApi = class {
16933
17056
  return this.identity;
16934
17057
  }
16935
17058
  };
17059
+ function normalizeOAuthScanUrl(value, baseUrl) {
17060
+ if (typeof value !== "string" || TERMINAL_CONTROL_CHARACTERS.test(value)) return void 0;
17061
+ try {
17062
+ const normalized = new URL(value).href;
17063
+ return normalized.startsWith(`${baseUrl}/`) ? normalized : void 0;
17064
+ } catch {
17065
+ return void 0;
17066
+ }
17067
+ }
16936
17068
  var ClientServerApi = class extends HostServerApi {
16937
17069
  constructor(serverUrl, store, fetchImplementation = fetch) {
16938
17070
  super(serverUrl, store, fetchImplementation, "client");
16939
17071
  }
16940
17072
  };
16941
17073
  var ServerApiError = class extends Error {
16942
- constructor(code, message, retryable, status) {
17074
+ constructor(code, message, retryable, status2) {
16943
17075
  super(message);
16944
17076
  this.code = code;
16945
17077
  this.retryable = retryable;
16946
- this.status = status;
17078
+ this.status = status2;
16947
17079
  }
16948
17080
  };
16949
17081
  function validateTokens(value) {
@@ -16974,12 +17106,12 @@ async function parseBody(response) {
16974
17106
  throw new ServerApiError("INVALID_MESSAGE", "The Server returned invalid JSON.", false, response.status);
16975
17107
  }
16976
17108
  }
16977
- function mapStatus(status) {
16978
- if (status === 401) return "AUTH_INVALID";
16979
- if (status === 403) return "AUTH_REQUIRED";
16980
- if (status === 404) return "DEVICE_NOT_FOUND";
16981
- if (status === 429) return "RATE_LIMITED";
16982
- return status >= 500 ? "CONNECTION_FAILED" : "INVALID_MESSAGE";
17109
+ function mapStatus(status2) {
17110
+ if (status2 === 401) return "AUTH_INVALID";
17111
+ if (status2 === 403) return "AUTH_REQUIRED";
17112
+ if (status2 === 404) return "DEVICE_NOT_FOUND";
17113
+ if (status2 === 429) return "RATE_LIMITED";
17114
+ return status2 >= 500 ? "CONNECTION_FAILED" : "INVALID_MESSAGE";
16983
17115
  }
16984
17116
  function parseHostDevice(value) {
16985
17117
  const item = requireRecord(value, "host device");
@@ -17281,10 +17413,10 @@ function isUsableExternalNode(candidate, requireFrom) {
17281
17413
  }
17282
17414
  function isExecutableFile(candidate) {
17283
17415
  try {
17284
- const stat4 = statSync(candidate);
17285
- if (!stat4.isFile()) return false;
17416
+ const stat6 = statSync(candidate);
17417
+ if (!stat6.isFile()) return false;
17286
17418
  if (process.platform === "win32") return true;
17287
- return (stat4.mode & 73) !== 0;
17419
+ return (stat6.mode & 73) !== 0;
17288
17420
  } catch {
17289
17421
  return false;
17290
17422
  }
@@ -17373,8 +17505,8 @@ var ExternalNativePeerConnection = class {
17373
17505
  request(method, payload) {
17374
17506
  if (this.closed) return Promise.reject(new Error("native rtc helper is closed"));
17375
17507
  const id2 = this.nextRequestId++;
17376
- const promise = new Promise((resolve2, reject) => {
17377
- this.pending.set(id2, { resolve: resolve2, reject });
17508
+ const promise = new Promise((resolve3, reject) => {
17509
+ this.pending.set(id2, { resolve: resolve3, reject });
17378
17510
  });
17379
17511
  this.write({ id: id2, method, payload });
17380
17512
  return promise;
@@ -18168,10 +18300,10 @@ function parseRouteTarget(value) {
18168
18300
  async function detectRouteHostIpv4(target, timeoutMs) {
18169
18301
  const socket = createSocket("udp4");
18170
18302
  try {
18171
- return await new Promise((resolve2, reject) => {
18303
+ return await new Promise((resolve3, reject) => {
18172
18304
  const timer = setTimeout(() => {
18173
18305
  cleanup();
18174
- resolve2(void 0);
18306
+ resolve3(void 0);
18175
18307
  }, timeoutMs);
18176
18308
  const cleanup = () => {
18177
18309
  clearTimeout(timer);
@@ -18185,7 +18317,7 @@ async function detectRouteHostIpv4(target, timeoutMs) {
18185
18317
  socket.connect(target.port, target.host, () => {
18186
18318
  cleanup();
18187
18319
  const address = socket.address();
18188
- resolve2(typeof address === "string" ? void 0 : address.address);
18320
+ resolve3(typeof address === "string" ? void 0 : address.address);
18189
18321
  });
18190
18322
  });
18191
18323
  } finally {
@@ -19139,7 +19271,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
19139
19271
  }
19140
19272
  async function waitForCodexFrames(stream, signal) {
19141
19273
  if (signal?.aborted) throw new ClientModeError("RPC_ABORTED", "The Codex event poll was cancelled.");
19142
- await new Promise((resolve2, reject) => {
19274
+ await new Promise((resolve3, reject) => {
19143
19275
  const previousWake = stream.wake;
19144
19276
  const timer = setTimeout(done, 25e3);
19145
19277
  const onAbort = () => {
@@ -19153,7 +19285,7 @@ async function waitForCodexFrames(stream, signal) {
19153
19285
  }
19154
19286
  function done() {
19155
19287
  cleanup();
19156
- resolve2();
19288
+ resolve3();
19157
19289
  }
19158
19290
  stream.wake = () => {
19159
19291
  previousWake();
@@ -20368,7 +20500,7 @@ var HostServerConnection = class {
20368
20500
  this.controlFrameLimits = {};
20369
20501
  let acknowledged = false;
20370
20502
  let messageQueue = Promise.resolve();
20371
- await new Promise((resolve2, reject) => {
20503
+ await new Promise((resolve3, reject) => {
20372
20504
  let settled = false;
20373
20505
  const helloTimer = setTimeout(() => socket.close(4001, "hello timeout"), 1e4);
20374
20506
  const finish = (error) => {
@@ -20377,7 +20509,7 @@ var HostServerConnection = class {
20377
20509
  clearTimeout(helloTimer);
20378
20510
  this.online = false;
20379
20511
  if (this.socket === socket) this.socket = void 0;
20380
- void this.dropTunnels().finally(() => error === void 0 ? resolve2() : reject(error));
20512
+ void this.dropTunnels().finally(() => error === void 0 ? resolve3() : reject(error));
20381
20513
  };
20382
20514
  socket.onopen = () => {
20383
20515
  this.sendControl("hello", {
@@ -20895,14 +21027,14 @@ var HostServerConnection = class {
20895
21027
  waitBeforeRetry(baseDelay) {
20896
21028
  const spread = baseDelay * this.config.reconnect.jitter;
20897
21029
  const delay = Math.max(0, Math.round(baseDelay - spread + Math.random() * spread * 2));
20898
- return new Promise((resolve2) => {
21030
+ return new Promise((resolve3) => {
20899
21031
  const timer = setTimeout(() => {
20900
21032
  this.retryWake = void 0;
20901
- resolve2();
21033
+ resolve3();
20902
21034
  }, delay);
20903
21035
  this.retryWake = () => {
20904
21036
  clearTimeout(timer);
20905
- resolve2();
21037
+ resolve3();
20906
21038
  };
20907
21039
  });
20908
21040
  }
@@ -21868,7 +22000,7 @@ function historyRequestPayload(payload, maxMessages) {
21868
22000
  return { ...payload, maxMessages };
21869
22001
  }
21870
22002
  function withTimeout(promise, ms, message) {
21871
- return new Promise((resolve2, reject) => {
22003
+ return new Promise((resolve3, reject) => {
21872
22004
  const timer = setTimeout(() => {
21873
22005
  reject(new RpcError("TIMEOUT", message, void 0, true));
21874
22006
  }, ms);
@@ -21876,7 +22008,7 @@ function withTimeout(promise, ms, message) {
21876
22008
  promise.then(
21877
22009
  (value) => {
21878
22010
  clearTimeout(timer);
21879
- resolve2(value);
22011
+ resolve3(value);
21880
22012
  },
21881
22013
  (error) => {
21882
22014
  clearTimeout(timer);
@@ -22206,8 +22338,9 @@ function concatChunks2(chunks, totalBytes) {
22206
22338
 
22207
22339
  // src/codex/domain.ts
22208
22340
  import { randomUUID } from "node:crypto";
22341
+ import { realpath, stat as stat4 } from "node:fs/promises";
22209
22342
  import { homedir as homedir3 } from "node:os";
22210
- import { isAbsolute as isAbsolute3, join as join5 } from "node:path";
22343
+ import { isAbsolute as isAbsolute3, join as join5, resolve as resolve2 } from "node:path";
22211
22344
 
22212
22345
  // src/codex/app-server.ts
22213
22346
  import { spawn as spawn2 } from "node:child_process";
@@ -22280,15 +22413,15 @@ var CodexAppServerClient = class {
22280
22413
  const child = this.process;
22281
22414
  this.process = void 0;
22282
22415
  if (child === void 0 || child.exitCode !== null || child.killed) return;
22283
- await new Promise((resolve2) => {
22416
+ await new Promise((resolve3) => {
22284
22417
  const timer = setTimeout(() => {
22285
22418
  child.kill("SIGKILL");
22286
- resolve2();
22419
+ resolve3();
22287
22420
  }, 2e3);
22288
22421
  timer.unref?.();
22289
22422
  child.once("exit", () => {
22290
22423
  clearTimeout(timer);
22291
- resolve2();
22424
+ resolve3();
22292
22425
  });
22293
22426
  child.kill("SIGTERM");
22294
22427
  });
@@ -22344,13 +22477,13 @@ var CodexAppServerClient = class {
22344
22477
  }
22345
22478
  request(method, params, timeoutMs) {
22346
22479
  const id2 = this.nextId++;
22347
- const result = new Promise((resolve2, reject) => {
22480
+ const result = new Promise((resolve3, reject) => {
22348
22481
  const timer = setTimeout(() => {
22349
22482
  this.pending.delete(id2);
22350
22483
  reject(new CodexAppServerError("CODEX_REQUEST_TIMEOUT", "Codex App Server request timed out."));
22351
22484
  }, timeoutMs);
22352
22485
  timer.unref?.();
22353
- this.pending.set(id2, { resolve: resolve2, reject, timer });
22486
+ this.pending.set(id2, { resolve: resolve3, reject, timer });
22354
22487
  });
22355
22488
  try {
22356
22489
  this.write({ id: id2, method, params });
@@ -22825,6 +22958,8 @@ function concatChunks3(chunks, totalBytes) {
22825
22958
  // src/codex/domain.ts
22826
22959
  var APPROVAL_TTL_MS = 5 * 6e4;
22827
22960
  var DEFAULT_RESTART_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
22961
+ var CODEX_PAGE_LIMIT2 = 100;
22962
+ var MAX_CODEX_PAGES2 = 32;
22828
22963
  var CODEX_HISTORY_PAGE_LIMIT = 25;
22829
22964
  var MAX_CODEX_HISTORY_PAGES = 64;
22830
22965
  var CodexRemoteDomain = class {
@@ -22838,6 +22973,7 @@ var CodexRemoteDomain = class {
22838
22973
  unsubscribeInbound;
22839
22974
  unsubscribeUnavailable;
22840
22975
  peers = /* @__PURE__ */ new Map();
22976
+ peerDeviceIds = /* @__PURE__ */ new Map();
22841
22977
  turnOwners = /* @__PURE__ */ new Map();
22842
22978
  approvals = /* @__PURE__ */ new Map();
22843
22979
  approvalExpiryTimer;
@@ -22877,6 +23013,7 @@ var CodexRemoteDomain = class {
22877
23013
  if (!this.config.enabled) return void 0;
22878
23014
  const bridge = new CodexPeerBridge(this, context, publish, this.logger);
22879
23015
  this.peers.set(context.connectionId, bridge);
23016
+ this.peerDeviceIds.set(context.connectionId, context.peerDeviceId);
22880
23017
  return bridge;
22881
23018
  }
22882
23019
  async call(connectionId, input2) {
@@ -22890,19 +23027,19 @@ var CodexRemoteDomain = class {
22890
23027
  return sanitizeProjectList(await this.callUpstream(call.method, call.params));
22891
23028
  }
22892
23029
  if (call.method === "thread/list") {
22893
- return filterThreadListByProjectAuthority(
22894
- sanitizeThreadList(await this.callUpstream(call.method, call.params)),
22895
- await this.readProjectAuthority()
23030
+ const result = sanitizeThreadList(await this.callUpstream(call.method, call.params));
23031
+ return filterThreadListByWorkspaceAuthority(
23032
+ result,
23033
+ await this.readWorkspaceAuthority(result.data)
22896
23034
  );
22897
23035
  }
22898
23036
  if (call.method === "thread/start") {
22899
23037
  const cwd = await this.requireCodexWorkspacePath(call.params.cwd);
22900
- const permission = codexPermission(call.params);
23038
+ const permission = codexPermission(call.params, "workspace-write");
22901
23039
  const result = await this.callUpstream(call.method, {
22902
23040
  ...permission.params,
22903
23041
  cwd,
22904
- approvalPolicy: permission.approvalPolicy,
22905
- sandbox: permission.sandbox,
23042
+ ...codexThreadPermissionParams(permission),
22906
23043
  serviceName: "deepseek_harness_remote"
22907
23044
  });
22908
23045
  if (extractThread(result)?.id === void 0) {
@@ -22913,7 +23050,7 @@ var CodexRemoteDomain = class {
22913
23050
  const threadId = threadIdFromParams(call.params);
22914
23051
  if (call.method === "dsh/sessionHistory") {
22915
23052
  const thread = await this.readThreadForHistory(connectionId, threadId);
22916
- return paginateCodexNativeHistory(
23053
+ const page = paginateCodexNativeHistory(
22917
23054
  projectCodexNativeHistory(thread, `codex:${threadId}`),
22918
23055
  {
22919
23056
  beforeSeq: optionalInteger2(call.params.beforeSeq),
@@ -22921,6 +23058,8 @@ var CodexRemoteDomain = class {
22921
23058
  maxMessages: optionalInteger2(call.params.maxMessages)
22922
23059
  }
22923
23060
  );
23061
+ const activeTurnId2 = typeof page.activeTurnId === "string" ? page.activeTurnId : this.turnOwners.get(threadId)?.turnId;
23062
+ return activeTurnId2 === void 0 ? page : { ...page, activeTurnId: activeTurnId2 };
22924
23063
  }
22925
23064
  const allowedThread = threadId === void 0 ? void 0 : await this.readKnownThread(threadId);
22926
23065
  if (call.method === "thread/read") {
@@ -22933,39 +23072,38 @@ var CodexRemoteDomain = class {
22933
23072
  return this.callUpstream(call.method, call.params);
22934
23073
  }
22935
23074
  let claimed = false;
23075
+ let previousOwner;
22936
23076
  if (isThreadMutation(call.method) && threadId !== void 0) {
22937
- claimed = this.claimTurn(threadId, connectionId, call.method);
23077
+ const claim = this.claimTurn(
23078
+ threadId,
23079
+ connectionId,
23080
+ call.method,
23081
+ typeof call.params.turnId === "string" ? call.params.turnId : void 0
23082
+ );
23083
+ claimed = claim.claimed;
23084
+ previousOwner = claim.previous;
22938
23085
  }
22939
23086
  try {
22940
23087
  const permission = codexPermission(call.params);
22941
23088
  const upstreamParams = call.method === "thread/resume" && allowedThread !== void 0 ? {
22942
23089
  ...permission.params,
22943
23090
  ...allowedThread.cwd === void 0 ? {} : { cwd: allowedThread.cwd },
22944
- approvalPolicy: permission.approvalPolicy,
22945
- sandbox: permission.sandbox,
23091
+ ...codexThreadPermissionParams(permission),
22946
23092
  excludeTurns: true
22947
23093
  } : call.method === "thread/fork" && allowedThread !== void 0 ? {
22948
23094
  ...permission.params,
22949
23095
  ...allowedThread.cwd === void 0 ? {} : { cwd: allowedThread.cwd },
22950
- approvalPolicy: permission.approvalPolicy,
22951
- sandbox: permission.sandbox
23096
+ ...codexThreadPermissionParams(permission)
22952
23097
  } : call.method === "turn/start" ? {
22953
23098
  ...permission.params,
22954
23099
  ...allowedThread?.cwd === void 0 ? {} : { cwd: allowedThread.cwd },
22955
- approvalPolicy: permission.approvalPolicy,
22956
- sandboxPolicy: permission.sandbox === "danger-full-access" ? { type: "dangerFullAccess" } : {
22957
- type: "workspaceWrite",
22958
- writableRoots: allowedThread?.cwd === void 0 ? [] : [allowedThread.cwd],
22959
- networkAccess: false,
22960
- excludeTmpdirEnvVar: false,
22961
- excludeSlashTmp: false
22962
- }
23100
+ ...codexTurnPermissionParams(permission, allowedThread?.cwd)
22963
23101
  } : permission.params;
22964
23102
  let result;
22965
23103
  try {
22966
23104
  result = await this.callUpstream(call.method, upstreamParams);
22967
23105
  } catch (error) {
22968
- if (call.method === "thread/resume" && allowedThread !== void 0 && error instanceof RpcError && error.code === "CODEX_UPSTREAM_ERROR") {
23106
+ if (call.method === "thread/resume" && allowedThread !== void 0 && error instanceof RpcError && error.code === "CODEX_UPSTREAM_ERROR" && call.params.permissionPreset === void 0) {
22969
23107
  return { thread: allowedThread.thread };
22970
23108
  }
22971
23109
  throw error;
@@ -22973,9 +23111,15 @@ var CodexRemoteDomain = class {
22973
23111
  if (call.method === "thread/resume" || call.method === "thread/fork" || call.method === "thread/unarchive") {
22974
23112
  await this.assertResultThreadAllowed(result);
22975
23113
  }
23114
+ if (call.method === "turn/start" && threadId !== void 0) {
23115
+ this.rememberTurnId(threadId, connectionId, extractTurnId(result));
23116
+ }
22976
23117
  return result;
22977
23118
  } catch (error) {
22978
- if (claimed && threadId !== void 0) this.turnOwners.delete(threadId);
23119
+ if (claimed && threadId !== void 0) {
23120
+ if (previousOwner === void 0) this.turnOwners.delete(threadId);
23121
+ else this.turnOwners.set(threadId, previousOwner);
23122
+ }
22979
23123
  throw mapAppServerError(error);
22980
23124
  }
22981
23125
  }
@@ -22994,8 +23138,9 @@ var CodexRemoteDomain = class {
22994
23138
  async detachPeer(connectionId) {
22995
23139
  const bridge = this.peers.get(connectionId);
22996
23140
  if (bridge !== void 0) this.peers.delete(connectionId);
23141
+ this.peerDeviceIds.delete(connectionId);
22997
23142
  for (const [threadId, owner] of this.turnOwners) {
22998
- if (owner === connectionId) this.turnOwners.delete(threadId);
23143
+ if (owner.connectionId === connectionId) this.turnOwners.delete(threadId);
22999
23144
  }
23000
23145
  const appServer = this.appServer;
23001
23146
  const pending = [...this.approvals.entries()].filter(([, approval]) => approval.connectionId === connectionId);
@@ -23014,6 +23159,7 @@ var CodexRemoteDomain = class {
23014
23159
  this.restartTimer = void 0;
23015
23160
  for (const bridge of [...this.peers.values()]) await bridge.closeAll();
23016
23161
  this.peers.clear();
23162
+ this.peerDeviceIds.clear();
23017
23163
  this.turnOwners.clear();
23018
23164
  if (this.approvalExpiryTimer !== void 0) clearTimeout(this.approvalExpiryTimer);
23019
23165
  this.approvalExpiryTimer = void 0;
@@ -23133,6 +23279,13 @@ var CodexRemoteDomain = class {
23133
23279
  }
23134
23280
  const threadId = extractThreadId(message.params);
23135
23281
  if (message.method === "turn/completed" && threadId !== void 0) this.turnOwners.delete(threadId);
23282
+ if (message.method === "turn/started" && threadId !== void 0) {
23283
+ const owner = this.turnOwners.get(threadId);
23284
+ const turnId = extractTurnId(message.params);
23285
+ if (owner !== void 0) {
23286
+ this.turnOwners.set(threadId, { ...owner, ...turnId === void 0 ? {} : { turnId } });
23287
+ }
23288
+ }
23136
23289
  if (message.method === "serverRequest/resolved") this.resolveUpstreamApproval(message.params);
23137
23290
  if (threadId === void 0) return;
23138
23291
  await Promise.all([...this.peers.values()].map((peer) => peer.publishInbound(threadId, {
@@ -23149,7 +23302,7 @@ var CodexRemoteDomain = class {
23149
23302
  }
23150
23303
  const threadId = extractThreadId(message.params);
23151
23304
  const owner = threadId === void 0 ? void 0 : this.turnOwners.get(threadId);
23152
- const peer = owner === void 0 ? void 0 : this.peers.get(owner);
23305
+ const peer = owner === void 0 ? void 0 : this.peers.get(owner.connectionId);
23153
23306
  if (threadId === void 0 || owner === void 0 || peer === void 0 || !peer.hasThreadSubscription(threadId)) {
23154
23307
  await appServer.respond(message.id, { decision: "decline" });
23155
23308
  return;
@@ -23157,7 +23310,7 @@ var CodexRemoteDomain = class {
23157
23310
  const requestHandle = randomUUID();
23158
23311
  this.approvals.set(requestHandle, {
23159
23312
  upstreamId: message.id,
23160
- connectionId: owner,
23313
+ connectionId: owner.connectionId,
23161
23314
  threadId,
23162
23315
  method: message.method,
23163
23316
  expiresAt: Date.now() + APPROVAL_TTL_MS
@@ -23176,7 +23329,7 @@ var CodexRemoteDomain = class {
23176
23329
  }
23177
23330
  }
23178
23331
  async readKnownThread(threadId) {
23179
- const authority = await this.readProjectAuthority();
23332
+ const authority = await this.readWorkspaceAuthority();
23180
23333
  let result;
23181
23334
  try {
23182
23335
  result = await this.callUpstream("thread/read", { threadId, includeTurns: false });
@@ -23189,7 +23342,7 @@ var CodexRemoteDomain = class {
23189
23342
  return { thread: listed, ...typeof listed.cwd === "string" && listed.cwd.length > 0 ? { cwd: listed.cwd } : {} };
23190
23343
  }
23191
23344
  const thread = extractThread(result);
23192
- if (thread === void 0 || thread.id !== threadId || !isThreadAllowedByProjectAuthority(thread, authority)) {
23345
+ if (thread === void 0 || thread.id !== threadId || !isThreadAllowedByWorkspaceAuthority(thread, authority)) {
23193
23346
  throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
23194
23347
  }
23195
23348
  return { thread, ...typeof thread.cwd === "string" && thread.cwd.length > 0 ? { cwd: thread.cwd } : {} };
@@ -23282,26 +23435,41 @@ var CodexRemoteDomain = class {
23282
23435
  if (thread === void 0 || typeof thread.id !== "string") {
23283
23436
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread.");
23284
23437
  }
23285
- if (!isThreadAllowedByProjectAuthority(thread, await this.readProjectAuthority())) {
23438
+ if (!isThreadAllowedByWorkspaceAuthority(thread, await this.readWorkspaceAuthority())) {
23286
23439
  throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
23287
23440
  }
23288
23441
  }
23289
- claimTurn(threadId, connectionId, method) {
23442
+ claimTurn(threadId, connectionId, method, turnId) {
23290
23443
  const owner = this.turnOwners.get(threadId);
23444
+ const peerDeviceId = this.peerDeviceIds.get(connectionId) ?? connectionId;
23445
+ const nextOwner = { connectionId, peerDeviceId, ...turnId === void 0 ? {} : { turnId } };
23291
23446
  if (method === "turn/interrupt") {
23292
23447
  if (owner === void 0) {
23293
- this.turnOwners.set(threadId, connectionId);
23294
- return true;
23448
+ this.turnOwners.set(threadId, nextOwner);
23449
+ return { claimed: true };
23295
23450
  }
23296
- if (owner !== connectionId) throw new RpcError("CODEX_TURN_OWNED", "Only the connection that started this Codex turn can interrupt it.");
23297
- return false;
23451
+ if (owner.connectionId !== connectionId && owner.peerDeviceId !== peerDeviceId) {
23452
+ throw new RpcError("CODEX_TURN_OWNED", "Only the connection that started this Codex turn can interrupt it.");
23453
+ }
23454
+ if (owner.connectionId !== connectionId) {
23455
+ this.turnOwners.set(threadId, { ...owner, connectionId, peerDeviceId, ...turnId === void 0 ? {} : { turnId } });
23456
+ return { claimed: true, previous: owner };
23457
+ }
23458
+ if (turnId !== void 0 && owner.turnId === void 0) this.turnOwners.set(threadId, { ...owner, turnId });
23459
+ return { claimed: false };
23298
23460
  }
23299
- if (owner !== void 0 && owner !== connectionId) {
23461
+ if (owner !== void 0 && owner.connectionId !== connectionId && owner.peerDeviceId !== peerDeviceId) {
23300
23462
  throw new RpcError("CODEX_TURN_OWNED", "Another Remote connection owns the active Codex turn.");
23301
23463
  }
23302
- if (owner === connectionId) return false;
23303
- this.turnOwners.set(threadId, connectionId);
23304
- return true;
23464
+ if (owner?.connectionId === connectionId) return { claimed: false };
23465
+ this.turnOwners.set(threadId, owner === void 0 ? nextOwner : { ...owner, connectionId, peerDeviceId });
23466
+ return { claimed: true, previous: owner };
23467
+ }
23468
+ rememberTurnId(threadId, connectionId, turnId) {
23469
+ if (turnId === void 0) return;
23470
+ const owner = this.turnOwners.get(threadId);
23471
+ if (owner === void 0 || owner.connectionId !== connectionId) return;
23472
+ this.turnOwners.set(threadId, { ...owner, turnId });
23305
23473
  }
23306
23474
  hasSubscriber(threadId) {
23307
23475
  return [...this.peers.values()].some((peer) => peer.hasThreadSubscription(threadId));
@@ -23348,29 +23516,53 @@ var CodexRemoteDomain = class {
23348
23516
  throw mapAppServerError(error);
23349
23517
  }
23350
23518
  }
23351
- async readProjectAuthority() {
23519
+ async readWorkspaceAuthority(listedThreads) {
23352
23520
  const projectIds = /* @__PURE__ */ new Set();
23353
23521
  const roots = [];
23354
23522
  let cursor2;
23355
- for (let page = 0; page < 32; page += 1) {
23356
- const result = sanitizeProjectList(await this.callUpstream("project/list", {
23357
- limit: 100,
23358
- ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23359
- }));
23360
- for (const project of result.data) {
23361
- const projectRoots = project.roots.map((root) => root.path).filter((path) => isAbsolute3(path));
23362
- if (projectRoots.length === 0) continue;
23363
- projectIds.add(project.id);
23364
- roots.push(...projectRoots);
23523
+ try {
23524
+ for (let page = 0; page < MAX_CODEX_PAGES2; page += 1) {
23525
+ const result = sanitizeProjectList(await this.callUpstream("project/list", {
23526
+ limit: CODEX_PAGE_LIMIT2,
23527
+ ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23528
+ }));
23529
+ for (const project of result.data) {
23530
+ const projectRoots = project.roots.map((root) => root.path).filter((path) => isAbsolute3(path));
23531
+ if (projectRoots.length === 0) continue;
23532
+ projectIds.add(project.id);
23533
+ roots.push(...projectRoots);
23534
+ }
23535
+ cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
23536
+ if (cursor2 === void 0) break;
23365
23537
  }
23366
- cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
23367
- if (cursor2 === void 0) break;
23538
+ } catch (error) {
23539
+ if (!isProjectListFallbackError(error)) throw error;
23540
+ }
23541
+ if (roots.length > 0) return { projectIds, roots };
23542
+ const threads = listedThreads === void 0 ? [] : [...listedThreads];
23543
+ if (listedThreads === void 0) {
23544
+ cursor2 = void 0;
23545
+ for (let page = 0; page < MAX_CODEX_PAGES2; page += 1) {
23546
+ const result = sanitizeThreadList(await this.callUpstream("thread/list", {
23547
+ limit: CODEX_PAGE_LIMIT2,
23548
+ sortKey: "updated_at",
23549
+ sortDirection: "desc",
23550
+ archived: false,
23551
+ ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23552
+ }));
23553
+ threads.push(...result.data);
23554
+ cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
23555
+ if (cursor2 === void 0) break;
23556
+ }
23557
+ }
23558
+ for (const workspace of deriveCodexCwdWorkspaces(threads)) {
23559
+ if (isAbsolute3(workspace.path)) roots.push(workspace.path);
23368
23560
  }
23369
23561
  return { projectIds, roots };
23370
23562
  }
23371
23563
  async findKnownThreadInList(threadId, authority) {
23372
23564
  let cursor2;
23373
- for (let page = 0; page < 32; page += 1) {
23565
+ for (let page = 0; page < MAX_CODEX_PAGES2; page += 1) {
23374
23566
  const result = sanitizeThreadList(await this.callUpstream("thread/list", {
23375
23567
  limit: 100,
23376
23568
  sortKey: "updated_at",
@@ -23380,7 +23572,7 @@ var CodexRemoteDomain = class {
23380
23572
  }));
23381
23573
  const thread = result.data.find((item) => item.id === threadId);
23382
23574
  if (thread !== void 0) {
23383
- return isThreadAllowedByProjectAuthority(thread, authority) ? thread : void 0;
23575
+ return isThreadAllowedByWorkspaceAuthority(thread, authority) ? thread : void 0;
23384
23576
  }
23385
23577
  cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
23386
23578
  if (cursor2 === void 0) break;
@@ -23391,16 +23583,28 @@ var CodexRemoteDomain = class {
23391
23583
  if (!isAbsolute3(path)) {
23392
23584
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23393
23585
  }
23394
- const paths = await this.listCodexProjectWorkspacePaths();
23395
- const canonical = paths.get(normalizeCodexPathForCompare(path));
23396
- if (canonical === void 0) {
23586
+ const paths = await this.listCodexWorkspacePaths();
23587
+ const lexicalCandidate = resolve2(path);
23588
+ let candidate;
23589
+ try {
23590
+ candidate = await realpath(path);
23591
+ if (!(await stat4(candidate)).isDirectory()) throw new Error("not a directory");
23592
+ } catch {
23397
23593
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23398
23594
  }
23399
- return canonical;
23595
+ for (const root of paths.values()) {
23596
+ try {
23597
+ if (!containsCodexPath(resolve2(root), lexicalCandidate)) continue;
23598
+ const canonicalRoot = await realpath(root);
23599
+ if (containsCodexPath(canonicalRoot, candidate)) return lexicalCandidate;
23600
+ } catch {
23601
+ }
23602
+ }
23603
+ throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23400
23604
  }
23401
- async listCodexProjectWorkspacePaths() {
23605
+ async listCodexWorkspacePaths() {
23402
23606
  const paths = /* @__PURE__ */ new Map();
23403
- const authority = await this.readProjectAuthority();
23607
+ const authority = await this.readWorkspaceAuthority();
23404
23608
  for (const root of authority.roots) paths.set(normalizeCodexPathForCompare(root), root);
23405
23609
  return paths;
23406
23610
  }
@@ -23469,13 +23673,13 @@ function sanitizeThreadList(result) {
23469
23673
  ...typeof result.backwardsCursor === "string" && result.backwardsCursor.length > 0 ? { backwardsCursor: result.backwardsCursor } : {}
23470
23674
  };
23471
23675
  }
23472
- function filterThreadListByProjectAuthority(result, authority) {
23676
+ function filterThreadListByWorkspaceAuthority(result, authority) {
23473
23677
  if (!isRecord9(result) || !Array.isArray(result.data)) {
23474
23678
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
23475
23679
  }
23476
23680
  return {
23477
23681
  ...result,
23478
- data: result.data.map((record4) => isRecord9(record4) ? record4 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByProjectAuthority(thread, authority))
23682
+ data: result.data.map((record4) => isRecord9(record4) ? record4 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
23479
23683
  };
23480
23684
  }
23481
23685
  function sanitizeProjectList(result) {
@@ -23503,7 +23707,7 @@ function sanitizeProjectList(result) {
23503
23707
  ...typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? { nextCursor: result.nextCursor } : { nextCursor: null }
23504
23708
  };
23505
23709
  }
23506
- function isThreadAllowedByProjectAuthority(thread, authority) {
23710
+ function isThreadAllowedByWorkspaceAuthority(thread, authority) {
23507
23711
  const projectId = typeof thread.projectId === "string" ? thread.projectId : void 0;
23508
23712
  if (projectId !== void 0 && authority.projectIds.has(projectId)) return true;
23509
23713
  const cwd = typeof thread.cwd === "string" ? thread.cwd : void 0;
@@ -23520,6 +23724,12 @@ function normalizeCodexPathForCompare(path) {
23520
23724
  function extractThread(result) {
23521
23725
  return isRecord9(result) && isRecord9(result.thread) ? result.thread : void 0;
23522
23726
  }
23727
+ function extractTurnId(result) {
23728
+ if (!isRecord9(result)) return void 0;
23729
+ if (typeof result.turnId === "string" && result.turnId.length > 0) return result.turnId;
23730
+ if (isRecord9(result.turn) && typeof result.turn.id === "string" && result.turn.id.length > 0) return result.turn.id;
23731
+ return void 0;
23732
+ }
23523
23733
  function extractThreadId(params) {
23524
23734
  if (!isRecord9(params)) return void 0;
23525
23735
  if (typeof params.threadId === "string") return params.threadId;
@@ -23530,15 +23740,37 @@ function extractThreadId(params) {
23530
23740
  function optionalInteger2(value) {
23531
23741
  return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
23532
23742
  }
23533
- function codexPermission(params) {
23743
+ function codexPermission(params, fallbackPreset) {
23534
23744
  const { permissionPreset: permissionPreset2, ...rest } = params;
23535
- const fullAccess = permissionPreset2 === "danger-full-access";
23745
+ const preset = permissionPreset2 === "workspace-write" || permissionPreset2 === "danger-full-access" ? permissionPreset2 : fallbackPreset;
23746
+ if (preset === void 0) return { params: mapCodexImageInputs(rest) };
23747
+ const fullAccess = preset === "danger-full-access";
23536
23748
  return {
23537
23749
  params: mapCodexImageInputs(rest),
23538
23750
  approvalPolicy: fullAccess ? "never" : "on-request",
23539
23751
  sandbox: fullAccess ? "danger-full-access" : "workspace-write"
23540
23752
  };
23541
23753
  }
23754
+ function codexThreadPermissionParams(permission) {
23755
+ if (permission.approvalPolicy === void 0 || permission.sandbox === void 0) return {};
23756
+ return {
23757
+ approvalPolicy: permission.approvalPolicy,
23758
+ sandbox: permission.sandbox
23759
+ };
23760
+ }
23761
+ function codexTurnPermissionParams(permission, cwd) {
23762
+ if (permission.approvalPolicy === void 0 || permission.sandbox === void 0) return {};
23763
+ return {
23764
+ approvalPolicy: permission.approvalPolicy,
23765
+ sandboxPolicy: permission.sandbox === "danger-full-access" ? { type: "dangerFullAccess" } : {
23766
+ type: "workspaceWrite",
23767
+ writableRoots: cwd === void 0 ? [] : [cwd],
23768
+ networkAccess: false,
23769
+ excludeTmpdirEnvVar: false,
23770
+ excludeSlashTmp: false
23771
+ }
23772
+ };
23773
+ }
23542
23774
  function mapCodexImageInputs(params) {
23543
23775
  if (!Array.isArray(params.input)) return params;
23544
23776
  return {
@@ -23575,6 +23807,9 @@ function errorCode3(error) {
23575
23807
  function isHistoryReadRecoverable(error) {
23576
23808
  return error instanceof RpcError && ["METHOD_NOT_ALLOWED", "METHOD_NOT_FOUND", "CODEX_UPSTREAM_ERROR", "CODEX_REQUEST_TIMEOUT", "CODEX_THREAD_BUSY"].includes(error.code);
23577
23809
  }
23810
+ function isProjectListFallbackError(error) {
23811
+ return error instanceof RpcError && ["METHOD_NOT_ALLOWED", "METHOD_NOT_FOUND", "CODEX_UPSTREAM_ERROR"].includes(error.code);
23812
+ }
23578
23813
  function canTryNextBinary(error) {
23579
23814
  return !(error instanceof RpcError) || !["CODEX_AUTH_REQUIRED", "CODEX_CLOSED"].includes(error.code);
23580
23815
  }
@@ -23693,13 +23928,42 @@ var HostPluginRuntime = class {
23693
23928
  }
23694
23929
  this.serverConnection.reconnect();
23695
23930
  }
23931
+ async startHostOAuthQrLogin(provider) {
23932
+ if (this.serverApi === void 0) {
23933
+ throw new ServerApiError("SERVER_NOT_CONFIGURED", "Remote Host Server is unavailable.", false);
23934
+ }
23935
+ return this.serverApi.startOAuthQrLogin(provider);
23936
+ }
23937
+ async pollHostOAuthQrLogin(qrId) {
23938
+ if (this.serverApi === void 0) {
23939
+ throw new ServerApiError("SERVER_NOT_CONFIGURED", "Remote Host Server is unavailable.", false);
23940
+ }
23941
+ const result = await this.serverApi.pollOAuthQrLogin(this.currentIdentity(), qrId, async () => {
23942
+ await this.serverConnection?.stop();
23943
+ this.identity = await this.identities.reset(this.config.deviceName);
23944
+ this.serverApi.bindIdentity(this.identity);
23945
+ this.serverConnection = this.createServerConnection(this.identity);
23946
+ return this.identity;
23947
+ });
23948
+ if (result.status === "complete") {
23949
+ this.serverConnection?.reconnect();
23950
+ this.logger.info("Host account authorized through OAuth QR login");
23951
+ }
23952
+ return result;
23953
+ }
23696
23954
  async clearHostAuthorization() {
23697
23955
  await this.serverConnection?.stop();
23698
- await this.serverApi?.revokeCurrentDevice();
23956
+ let revokeFailure;
23957
+ try {
23958
+ await this.serverApi?.revokeCurrentDevice();
23959
+ } catch (error) {
23960
+ revokeFailure = error;
23961
+ }
23699
23962
  this.identity = await this.identities.reset(this.config.deviceName);
23700
23963
  this.serverApi?.bindIdentity(this.identity);
23701
23964
  if (this.serverApi !== void 0) this.serverConnection = this.createServerConnection(this.identity);
23702
23965
  this.logger.info("Host authorization cleared");
23966
+ if (revokeFailure !== void 0) throw revokeFailure;
23703
23967
  }
23704
23968
  async authorizeHostAsOwned(accessToken, account) {
23705
23969
  if (this.serverApi === void 0) {
@@ -23787,6 +24051,7 @@ var HostPluginRuntime = class {
23787
24051
  peerDeviceId: this.connections.peerDeviceId() === void 0 ? void 0 : shortId5(this.connections.peerDeviceId()),
23788
24052
  peerDeviceIds: this.connections.peerDeviceIds().map(shortId5),
23789
24053
  trustedPeers: this.identities.listTrustedPeers().length,
24054
+ capabilities: this.hostCapabilities(),
23790
24055
  codex: this.codex.status()
23791
24056
  };
23792
24057
  }
@@ -23806,12 +24071,12 @@ var HostPluginRuntime = class {
23806
24071
  }
23807
24072
  async readHarnessVersion() {
23808
24073
  let reportedVersion;
23809
- let errorCode4;
24074
+ let errorCode5;
23810
24075
  try {
23811
24076
  const response = await this.apiProxy?.host.describe({ rpcId: randomUUID2(), payload: {} });
23812
24077
  if (response === void 0) throw new Error("ApiProxy is unavailable");
23813
24078
  if (!response.result.ok) {
23814
- errorCode4 = response.result.error.code;
24079
+ errorCode5 = response.result.error.code;
23815
24080
  } else {
23816
24081
  reportedVersion = normalizeHarnessVersion(response.result.value.version);
23817
24082
  }
@@ -23820,7 +24085,7 @@ var HostPluginRuntime = class {
23820
24085
  const distributionVersion = reportedVersion === void 0 || reportedVersion === "0.0.1" ? await readHarnessDistributionVersion() : void 0;
23821
24086
  const version = selectHarnessVersion(reportedVersion, distributionVersion);
23822
24087
  if (version !== void 0) return version;
23823
- this.logger.warn("Harness version is unavailable", errorCode4 === void 0 ? void 0 : { code: errorCode4 });
24088
+ this.logger.warn("Harness version is unavailable", errorCode5 === void 0 ? void 0 : { code: errorCode5 });
23824
24089
  return void 0;
23825
24090
  }
23826
24091
  hostCapabilities() {
@@ -23857,6 +24122,618 @@ function isPlainRecord(value) {
23857
24122
  return typeof value === "object" && value !== null && !Array.isArray(value);
23858
24123
  }
23859
24124
 
24125
+ // src/cli.ts
24126
+ import { stat as stat5 } from "node:fs/promises";
24127
+ import { hostname as hostname3 } from "node:os";
24128
+ import { join as join6 } from "node:path";
24129
+ var QR_POLL_INTERVAL_MS = 2e3;
24130
+ var TERMINAL_QR_MARGIN = 4;
24131
+ async function runCli(args = process.argv.slice(2), dependencies = {}) {
24132
+ const runtime = resolveDependencies(dependencies);
24133
+ const [command, ...rest] = args;
24134
+ try {
24135
+ if (command === "login") return await login(rest, runtime);
24136
+ if (command === "status") return await status(rest, runtime);
24137
+ if (command === "logout") return await logout(rest, runtime);
24138
+ if (command === "help" || command === "--help" || command === "-h" || command === void 0) {
24139
+ write(runtime.stdout, helpText());
24140
+ return 0;
24141
+ }
24142
+ throw new CliUsageError(`Unknown command: ${command}`);
24143
+ } catch (error) {
24144
+ write(runtime.stderr, `${cliErrorMessage(error)}
24145
+ `);
24146
+ if (error instanceof CliUsageError) write(runtime.stderr, `
24147
+ ${helpText()}`);
24148
+ return error instanceof CliUsageError ? 2 : 1;
24149
+ }
24150
+ }
24151
+ async function login(args, runtime) {
24152
+ if (args.length > 1) throw new CliUsageError("Usage: ds-harness-remote login [github|zhihu]");
24153
+ const provider = args[0] ?? "zhihu";
24154
+ if (provider !== "github" && provider !== "zhihu") {
24155
+ throw new CliUsageError("Login provider must be github or zhihu.");
24156
+ }
24157
+ const context = await hostContext(runtime);
24158
+ const session = await context.api.startOAuthQrLogin(provider);
24159
+ const qr = await runtime.renderQr(session.scanUrl);
24160
+ const providerName = provider === "github" ? "GitHub" : "Zhihu";
24161
+ write(runtime.stdout, `Using ${providerName} QR login. ${alternativeProviderHint(provider)}
24162
+ `);
24163
+ write(runtime.stdout, "Scan this QR code to authorize this Host:\n\n");
24164
+ write(runtime.stdout, `${qr.trimEnd()}
24165
+ `);
24166
+ write(runtime.stdout, `Authorization URL: ${terminalLink(session.scanUrl, runtime.stdout)}
24167
+
24168
+ Waiting for authorization...
24169
+ `);
24170
+ const deadline = runtime.now() + session.expiresIn * 1e3;
24171
+ let identity = context.identity;
24172
+ while (runtime.now() < deadline) {
24173
+ let result;
24174
+ try {
24175
+ result = await context.api.pollOAuthQrLogin(identity, session.qrId, async () => {
24176
+ identity = await context.identities.reset(context.deviceName);
24177
+ return identity;
24178
+ });
24179
+ } catch (error) {
24180
+ if (!(error instanceof ServerApiError) || !error.retryable) throw error;
24181
+ await runtime.wait(Math.min(QR_POLL_INTERVAL_MS, Math.max(1, deadline - runtime.now())));
24182
+ continue;
24183
+ }
24184
+ if (result.status === "complete") {
24185
+ write(runtime.stdout, `${authorizedMessage(result.authorization)} Restart dsh-tui to bring the Remote Host online.
24186
+ `);
24187
+ return 0;
24188
+ }
24189
+ if (result.status === "expired") break;
24190
+ await runtime.wait(Math.min(QR_POLL_INTERVAL_MS, Math.max(1, deadline - runtime.now())));
24191
+ }
24192
+ write(runtime.stderr, "The QR login expired. Run the login command again to generate a new code.\n");
24193
+ return 1;
24194
+ }
24195
+ async function status(args, runtime) {
24196
+ if (args.length !== 0) throw new CliUsageError("Usage: ds-harness-remote status");
24197
+ const serverUrl = selectedServer();
24198
+ const root = new IdentityStore({ env: runtime.env }).directory;
24199
+ const directory = serverStorageDirectory(root, serverUrl, "host");
24200
+ const lines = [
24201
+ "Remote Host status",
24202
+ `Server: ${serverUrl}`,
24203
+ "Host control: enabled (dsh-TUI default)"
24204
+ ];
24205
+ if (!await exists3(join6(directory, "device.json"))) {
24206
+ lines.push("Device: not initialized", "Authorization: logged out", "Credential: unavailable");
24207
+ write(runtime.stdout, `${lines.join("\n")}
24208
+
24209
+ Run "ds-harness-remote login" or use "/remote login" in dsh-TUI.
24210
+ `);
24211
+ return 0;
24212
+ }
24213
+ const identities = runtime.createIdentityStore({ directory, env: runtime.env });
24214
+ const identity = await identities.loadOrCreate(hostname3());
24215
+ const store = new ServerCredentialStore(directory);
24216
+ const stored = await store.load(serverUrl, identity.deviceId);
24217
+ lines.push(`Device: ${identity.name} (${identity.deviceId})`);
24218
+ if (stored === void 0) {
24219
+ lines.push("Authorization: logged out", "Credential: unavailable");
24220
+ write(runtime.stdout, `${lines.join("\n")}
24221
+
24222
+ Run "ds-harness-remote login" or use "/remote login" in dsh-TUI.
24223
+ `);
24224
+ return 0;
24225
+ }
24226
+ lines.push(`Authorization: logged in (${authorizationLabel(stored.authorizationMethod)})`);
24227
+ if (stored.account !== void 0) lines.push(`Account: ${stored.account}`);
24228
+ const api = runtime.createHostApi(serverUrl, store);
24229
+ api.bindIdentity(identity);
24230
+ try {
24231
+ await api.authenticate(identity);
24232
+ lines.push("Credential: ready");
24233
+ lines.push("Presence: published while dsh-tui is running");
24234
+ write(runtime.stdout, `${lines.join("\n")}
24235
+ `);
24236
+ return 0;
24237
+ } catch (error) {
24238
+ lines.push(`Credential: unavailable (${statusErrorCode(error)})`);
24239
+ write(runtime.stdout, `${lines.join("\n")}
24240
+ `);
24241
+ return 1;
24242
+ }
24243
+ }
24244
+ async function logout(args, runtime) {
24245
+ if (args.length !== 0) throw new CliUsageError("Usage: ds-harness-remote logout");
24246
+ const serverUrl = selectedServer();
24247
+ const root = new IdentityStore({ env: runtime.env }).directory;
24248
+ const directory = serverStorageDirectory(root, serverUrl, "host");
24249
+ if (!await exists3(join6(directory, "device.json"))) {
24250
+ await new ServerCredentialStore(directory).clear();
24251
+ write(runtime.stdout, "This Host is already logged out.\n");
24252
+ return 0;
24253
+ }
24254
+ const deviceName = hostname3();
24255
+ const identities = runtime.createIdentityStore({ directory, env: runtime.env });
24256
+ const identity = await identities.loadOrCreate(deviceName);
24257
+ const api = runtime.createHostApi(serverUrl, new ServerCredentialStore(directory));
24258
+ api.bindIdentity(identity);
24259
+ let revokeFailure;
24260
+ try {
24261
+ await api.revokeCurrentDevice();
24262
+ } catch (error) {
24263
+ revokeFailure = error;
24264
+ }
24265
+ await identities.reset(deviceName);
24266
+ if (revokeFailure !== void 0) {
24267
+ throw new Error(`Local Host credentials were cleared, but Server revocation failed: ${cliErrorMessage(revokeFailure)}`);
24268
+ }
24269
+ write(runtime.stdout, "Remote Host logged out and its local device identity was rotated. Restart dsh-tui.\n");
24270
+ return 0;
24271
+ }
24272
+ async function hostContext(runtime) {
24273
+ const serverUrl = selectedServer();
24274
+ const root = new IdentityStore({ env: runtime.env }).directory;
24275
+ const directory = serverStorageDirectory(root, serverUrl, "host");
24276
+ const deviceName = hostname3();
24277
+ const identities = runtime.createIdentityStore({ directory, env: runtime.env });
24278
+ const identity = await identities.loadOrCreate(deviceName);
24279
+ const api = runtime.createHostApi(serverUrl, new ServerCredentialStore(directory));
24280
+ return { api, identities, identity, deviceName };
24281
+ }
24282
+ function selectedServer() {
24283
+ return normalizeServerUrl(DEFAULT_REMOTE_SERVER_URL);
24284
+ }
24285
+ function resolveDependencies(input2) {
24286
+ return {
24287
+ env: input2.env ?? process.env,
24288
+ stdout: input2.stdout ?? process.stdout,
24289
+ stderr: input2.stderr ?? process.stderr,
24290
+ now: input2.now ?? Date.now,
24291
+ wait: input2.wait ?? ((milliseconds) => new Promise((resolve3) => setTimeout(resolve3, milliseconds))),
24292
+ renderQr: input2.renderQr ?? renderTerminalQr,
24293
+ createIdentityStore: input2.createIdentityStore ?? ((options) => new IdentityStore(options)),
24294
+ createHostApi: input2.createHostApi ?? ((serverUrl, store) => new HostServerApi(serverUrl, store))
24295
+ };
24296
+ }
24297
+ async function renderTerminalQr(url) {
24298
+ const { default: QRCode } = await import("qrcode");
24299
+ const modules = QRCode.create(url, { errorCorrectionLevel: "M" }).modules;
24300
+ const width = modules.size + TERMINAL_QR_MARGIN * 2;
24301
+ const quietLine = `\x1B[47m${" ".repeat(width * 2)}\x1B[0m`;
24302
+ const lines = Array.from({ length: TERMINAL_QR_MARGIN }, () => quietLine);
24303
+ for (let row = 0; row < modules.size; row += 1) {
24304
+ let line = `\x1B[47m${" ".repeat(TERMINAL_QR_MARGIN * 2)}`;
24305
+ for (let column = 0; column < modules.size; column += 1) {
24306
+ line += modules.get(row, column) === 1 ? "\x1B[40m " : "\x1B[47m ";
24307
+ }
24308
+ line += `\x1B[47m${" ".repeat(TERMINAL_QR_MARGIN * 2)}\x1B[0m`;
24309
+ lines.push(line);
24310
+ }
24311
+ lines.push(...Array.from({ length: TERMINAL_QR_MARGIN }, () => quietLine));
24312
+ return lines.join("\n");
24313
+ }
24314
+ async function renderCompactTerminalQr(url) {
24315
+ const { default: QRCode } = await import("qrcode");
24316
+ const modules = QRCode.create(url, { errorCorrectionLevel: "M" }).modules;
24317
+ const size = modules.size + TERMINAL_QR_MARGIN * 2;
24318
+ const lines = [];
24319
+ const isBlack = (row, column) => {
24320
+ const qrRow = row - TERMINAL_QR_MARGIN;
24321
+ const qrColumn = column - TERMINAL_QR_MARGIN;
24322
+ return qrRow >= 0 && qrRow < modules.size && qrColumn >= 0 && qrColumn < modules.size && modules.get(qrRow, qrColumn) === 1;
24323
+ };
24324
+ for (let row = 0; row < size; row += 2) {
24325
+ let line = "\x1B[30;47m";
24326
+ for (let column = 0; column < size; column += 1) {
24327
+ const top = isBlack(row, column);
24328
+ const bottom = isBlack(row + 1, column);
24329
+ line += top ? bottom ? "\u2588" : "\u2580" : bottom ? "\u2584" : " ";
24330
+ }
24331
+ lines.push(`${line}\x1B[0m`);
24332
+ }
24333
+ return lines.join("\n");
24334
+ }
24335
+ function authorizedMessage(authorization) {
24336
+ return authorization.account === void 0 ? "Remote Host login complete." : `Remote Host login complete for ${authorization.account}.`;
24337
+ }
24338
+ function alternativeProviderHint(provider) {
24339
+ return provider === "zhihu" ? "GitHub is also supported: ds-harness-remote login github" : "Zhihu is also supported: ds-harness-remote login zhihu";
24340
+ }
24341
+ function authorizationLabel(method) {
24342
+ if (method === "host_registration_code") return "Host registration code";
24343
+ if (method === "owned_device") return "owned device";
24344
+ return "account";
24345
+ }
24346
+ function statusErrorCode(error) {
24347
+ return error instanceof ServerApiError ? error.code : "CONNECTION_FAILED";
24348
+ }
24349
+ function cliErrorMessage(error) {
24350
+ const message = error instanceof Error ? error.message : "The Remote command failed.";
24351
+ const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
24352
+ return code === void 0 ? message : `${message} (${code})`;
24353
+ }
24354
+ function helpText() {
24355
+ return [
24356
+ "Usage:",
24357
+ " ds-harness-remote login [github|zhihu]",
24358
+ " ds-harness-remote status",
24359
+ " ds-harness-remote logout",
24360
+ "",
24361
+ "Inside dsh-TUI, use /remote login, /remote status, or /remote logout.",
24362
+ "login defaults to Zhihu and authorizes this computer as a Remote Host with a terminal QR code.",
24363
+ `The Server is ${DEFAULT_REMOTE_SERVER_URL}.`,
24364
+ "Host configuration is not exposed by this CLI yet. Restart dsh-tui after login or logout.",
24365
+ ""
24366
+ ].join("\n");
24367
+ }
24368
+ function write(target, value) {
24369
+ target.write(value);
24370
+ }
24371
+ function terminalLink(url, target) {
24372
+ if (target.isTTY !== true) return url;
24373
+ return `\x1B]8;;${url}\x07${url}\x1B]8;;\x07`;
24374
+ }
24375
+ async function exists3(path) {
24376
+ try {
24377
+ await stat5(path);
24378
+ return true;
24379
+ } catch (error) {
24380
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
24381
+ throw error;
24382
+ }
24383
+ }
24384
+ var CliUsageError = class extends Error {
24385
+ };
24386
+
24387
+ // src/tui-command.ts
24388
+ var QR_POLL_INTERVAL_MS2 = 2e3;
24389
+ var REMOTE_LOGIN_SCENE_ID = "remote-login";
24390
+ var REMOTE_STATUS_SCENE_ID = "remote-status";
24391
+ function installTuiRemoteCommand(ctx, resolveTarget) {
24392
+ const tuiContext = ctx;
24393
+ const commands = tuiContext.get("commands", false);
24394
+ if (commands === void 0) return;
24395
+ const pluginHost = tuiContext.get("tuiPluginHost", false);
24396
+ const trees = tuiContext.get("tuiCommandTrees", false);
24397
+ const scenes = tuiContext.get("tuiScenes", false);
24398
+ const login2 = new RemoteLoginController(resolveTarget);
24399
+ const LoginScene = createRemoteLoginScene(login2);
24400
+ const StatusScene = createRemoteStatusScene(resolveTarget);
24401
+ tuiContext.effect(() => {
24402
+ const definition = {
24403
+ name: "remote",
24404
+ description: "Manage Remote Host login and connection status",
24405
+ input: { hint: "[status | login [github|zhihu] | logout]" },
24406
+ recordInput: false,
24407
+ handler: async ({ rawInput }) => {
24408
+ const args = rawInput.trim().split(/\s+/u).filter(Boolean);
24409
+ const command = args[0] ?? "status";
24410
+ if (command === "status" && args.length <= 1) {
24411
+ if (scenes?.open(REMOTE_STATUS_SCENE_ID) === true) return { kind: "success" };
24412
+ return { kind: "success", text: formatRemoteStatusInline(resolveTarget()) };
24413
+ }
24414
+ if (command === "login" && args.length <= 2) {
24415
+ const provider = args[1] ?? "zhihu";
24416
+ if (provider !== "github" && provider !== "zhihu") {
24417
+ return { kind: "error", text: "Usage: /remote login [github|zhihu]" };
24418
+ }
24419
+ if (scenes === void 0) {
24420
+ return {
24421
+ kind: "error",
24422
+ text: `The Remote login screen is unavailable. Run "ds-harness-remote login ${provider}" outside dsh-TUI, then restart it.`
24423
+ };
24424
+ }
24425
+ login2.start(provider);
24426
+ if (!scenes.open(REMOTE_LOGIN_SCENE_ID)) {
24427
+ login2.cancel();
24428
+ return { kind: "error", text: "The Remote login screen is unavailable." };
24429
+ }
24430
+ return { kind: "success" };
24431
+ }
24432
+ if (command === "logout" && args.length === 1) {
24433
+ const target = resolveTarget();
24434
+ if (target === void 0) return remoteNotReady();
24435
+ try {
24436
+ await target.runtime.clearHostAuthorization();
24437
+ return {
24438
+ kind: "success",
24439
+ text: "Remote Host logged out and its local device identity was rotated."
24440
+ };
24441
+ } catch (error) {
24442
+ return {
24443
+ kind: "error",
24444
+ text: `Local Remote Host credentials were cleared, but Server revocation failed (${errorCode4(error)}).`
24445
+ };
24446
+ }
24447
+ }
24448
+ if (command === "config") {
24449
+ return { kind: "error", text: "Host configuration is not supported yet." };
24450
+ }
24451
+ return { kind: "error", text: remoteCommandUsage() };
24452
+ }
24453
+ };
24454
+ const disposeCommand = registerRemoteCommand(tuiContext, commands, pluginHost, definition);
24455
+ const disposeTree = registerOptional(tuiContext, "command completion", () => trees?.register({
24456
+ root: "remote",
24457
+ descriptions: {
24458
+ en: "Manage Remote Host login and connection status",
24459
+ zh: "\u7BA1\u7406 Remote Host \u767B\u5F55\u548C\u8FDE\u63A5\u72B6\u6001"
24460
+ },
24461
+ children: remoteCommandChildren
24462
+ }));
24463
+ const disposeScene = registerOptional(tuiContext, "login scene", () => scenes?.register({
24464
+ id: REMOTE_LOGIN_SCENE_ID,
24465
+ title: "Remote Host login",
24466
+ component: LoginScene
24467
+ }, tuiContext));
24468
+ const disposeStatusScene = registerOptional(tuiContext, "status scene", () => scenes?.register({
24469
+ id: REMOTE_STATUS_SCENE_ID,
24470
+ title: "Remote Host status",
24471
+ component: StatusScene
24472
+ }, tuiContext));
24473
+ return () => {
24474
+ login2.cancel();
24475
+ disposeCommand();
24476
+ disposeTree();
24477
+ disposeScene();
24478
+ disposeStatusScene();
24479
+ };
24480
+ }, "ds-harness-remote: dsh-tui /remote command");
24481
+ }
24482
+ function registerRemoteCommand(ctx, commands, pluginHost, definition) {
24483
+ if (pluginHost === void 0) return commands.register(definition);
24484
+ try {
24485
+ return pluginHost.registerCommand(ctx, definition);
24486
+ } catch (error) {
24487
+ if (!hasErrorCode2(error, "COMPONENT_NOT_ADMITTED")) throw error;
24488
+ ctx.logger.debug("dsh-TUI Remote command is using the legacy command registry because Component admission is unavailable");
24489
+ return commands.register(definition);
24490
+ }
24491
+ }
24492
+ function hasErrorCode2(error, code) {
24493
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
24494
+ }
24495
+ function registerOptional(ctx, feature, register) {
24496
+ try {
24497
+ return register() ?? (() => {
24498
+ });
24499
+ } catch (error) {
24500
+ ctx.logger.warn(`dsh-TUI Remote ${feature} is unavailable`, { code: errorCode4(error) });
24501
+ return () => {
24502
+ };
24503
+ }
24504
+ }
24505
+ function remoteCommandChildren(canonicalPath) {
24506
+ if (canonicalPath.length === 1 && canonicalPath[0] === "remote") {
24507
+ return [
24508
+ {
24509
+ name: "status",
24510
+ description: "Show Remote Host authorization and connection status",
24511
+ descriptions: { zh: "\u67E5\u770B Remote Host \u6388\u6743\u548C\u8FDE\u63A5\u72B6\u6001" }
24512
+ },
24513
+ {
24514
+ name: "login",
24515
+ description: "Authorize this Host with a GitHub or Zhihu QR code",
24516
+ descriptions: { zh: "\u4F7F\u7528 GitHub \u6216\u77E5\u4E4E\u4E8C\u7EF4\u7801\u6388\u6743\u5F53\u524D Host" }
24517
+ },
24518
+ {
24519
+ name: "logout",
24520
+ description: "Revoke this Host and rotate its local identity",
24521
+ descriptions: { zh: "\u64A4\u9500\u5F53\u524D Host \u5E76\u8F6E\u6362\u672C\u5730\u8EAB\u4EFD" }
24522
+ }
24523
+ ];
24524
+ }
24525
+ if (canonicalPath.length === 2 && canonicalPath[0] === "remote" && canonicalPath[1] === "login") {
24526
+ return [
24527
+ { name: "zhihu", description: "Sign in with Zhihu (default)", descriptions: { zh: "\u4F7F\u7528\u77E5\u4E4E\u767B\u5F55\uFF08\u9ED8\u8BA4\uFF09" } },
24528
+ { name: "github", description: "Sign in with GitHub", descriptions: { zh: "\u4F7F\u7528 GitHub \u767B\u5F55" } }
24529
+ ];
24530
+ }
24531
+ return [];
24532
+ }
24533
+ function remoteStatusLines(target) {
24534
+ if (target === void 0) {
24535
+ return ["Remote Host is disabled or still starting. Check the ds-harness-remote settings and retry."];
24536
+ }
24537
+ const { runtime, config } = target;
24538
+ const status2 = runtime.hostStatus();
24539
+ const diagnostics = runtime.diagnostics();
24540
+ const codex = runtime.codexStatus();
24541
+ const capabilities = new Set(diagnostics.capabilities);
24542
+ const connection = status2.online ? "online" : status2.reconnecting ? "reconnecting" : status2.accountRequired ? "authorization required" : "offline";
24543
+ return [
24544
+ `Server: ${config.serverUrl ?? "not configured"}`,
24545
+ "Host control: enabled",
24546
+ `Device: ${status2.deviceId ?? "not initialized"}`,
24547
+ `Authorization: ${status2.authorized ? status2.account === void 0 ? "logged in" : `logged in (${status2.account})` : "logged out"}`,
24548
+ `Server connection: ${connection}`,
24549
+ `Harness Remote API: ${capabilities.has("harness.api.v1") ? "available (ApiProxy)" : capabilities.has("harness.remote.v1") ? "available (Typert Remote)" : "unavailable"}`,
24550
+ `Remote clients: ${diagnostics.activeConnections}`,
24551
+ `Codex Remote: ${codex.enabled ? codex.state : "disabled"}`,
24552
+ "",
24553
+ "Commands: /remote login [github|zhihu] \xB7 /remote status \xB7 /remote logout"
24554
+ ];
24555
+ }
24556
+ function formatRemoteStatusInline(target) {
24557
+ return remoteStatusLines(target).filter(Boolean).join(" \xB7 ");
24558
+ }
24559
+ function remoteCommandUsage() {
24560
+ return [
24561
+ "Usage:",
24562
+ " /remote",
24563
+ " /remote status",
24564
+ " /remote login [github|zhihu]",
24565
+ " /remote logout"
24566
+ ].join("\n");
24567
+ }
24568
+ function remoteNotReady() {
24569
+ return {
24570
+ kind: "error",
24571
+ text: "Remote Host is disabled or still starting. Check the ds-harness-remote settings and retry."
24572
+ };
24573
+ }
24574
+ var RemoteLoginController = class {
24575
+ constructor(resolveTarget) {
24576
+ this.resolveTarget = resolveTarget;
24577
+ }
24578
+ current = { phase: "idle", provider: "zhihu" };
24579
+ listeners = /* @__PURE__ */ new Set();
24580
+ attempt = 0;
24581
+ snapshot = () => this.current;
24582
+ subscribe = (listener) => {
24583
+ this.listeners.add(listener);
24584
+ return () => {
24585
+ this.listeners.delete(listener);
24586
+ };
24587
+ };
24588
+ start(provider) {
24589
+ const attempt = ++this.attempt;
24590
+ this.update({ phase: "loading", provider });
24591
+ void this.run(attempt, provider);
24592
+ }
24593
+ cancel() {
24594
+ this.attempt += 1;
24595
+ }
24596
+ async run(attempt, provider) {
24597
+ try {
24598
+ const target = this.resolveTarget();
24599
+ if (target === void 0) throw new Error("REMOTE_NOT_READY");
24600
+ const session = await target.runtime.startHostOAuthQrLogin(provider);
24601
+ const [qr, compactQr] = await Promise.all([
24602
+ renderTerminalQr(session.scanUrl),
24603
+ renderCompactTerminalQr(session.scanUrl)
24604
+ ]);
24605
+ if (attempt !== this.attempt) return;
24606
+ this.update({ phase: "waiting", provider, qr, compactQr, scanUrl: session.scanUrl });
24607
+ await this.poll(attempt, provider, session, target.runtime);
24608
+ } catch (error) {
24609
+ if (attempt !== this.attempt) return;
24610
+ this.update({ phase: "error", provider, error: errorCode4(error) });
24611
+ }
24612
+ }
24613
+ async poll(attempt, provider, session, runtime) {
24614
+ const deadline = Date.now() + session.expiresIn * 1e3;
24615
+ while (attempt === this.attempt && Date.now() < deadline) {
24616
+ try {
24617
+ const result = await runtime.pollHostOAuthQrLogin(session.qrId);
24618
+ if (attempt !== this.attempt) return;
24619
+ if (result.status === "complete") {
24620
+ this.update({ phase: "complete", provider, account: result.authorization.account });
24621
+ return;
24622
+ }
24623
+ if (result.status === "expired") break;
24624
+ } catch (error) {
24625
+ if (!(error instanceof ServerApiError) || !error.retryable) throw error;
24626
+ }
24627
+ await wait(Math.min(QR_POLL_INTERVAL_MS2, Math.max(1, deadline - Date.now())));
24628
+ }
24629
+ if (attempt === this.attempt) this.update({ phase: "expired", provider });
24630
+ }
24631
+ update(snapshot) {
24632
+ this.current = snapshot;
24633
+ for (const listener of this.listeners) listener(snapshot);
24634
+ }
24635
+ };
24636
+ function createRemoteLoginScene(controller) {
24637
+ return function RemoteLoginScene({ React, ui, close }) {
24638
+ const [snapshot, setSnapshot] = React.useState(controller.snapshot());
24639
+ const terminal = ui.useTerminalSize();
24640
+ React.useEffect(() => {
24641
+ const unsubscribe = controller.subscribe(setSnapshot);
24642
+ return () => {
24643
+ unsubscribe();
24644
+ controller.cancel();
24645
+ };
24646
+ }, []);
24647
+ ui.useInput((input2, key) => {
24648
+ if (key.escape === true || input2.toLowerCase() === "q") {
24649
+ controller.cancel();
24650
+ close();
24651
+ }
24652
+ });
24653
+ const provider = snapshot.provider === "github" ? "GitHub" : "Zhihu";
24654
+ const other = snapshot.provider === "github" ? "/remote login zhihu" : "/remote login github";
24655
+ const children = [
24656
+ React.createElement(ui.Text, { key: "title", bold: true, color: "accent" }, "Remote Host login"),
24657
+ React.createElement(ui.Text, { key: "provider" }, `Provider: ${provider} \xB7 You can also use ${other}`)
24658
+ ];
24659
+ if (snapshot.phase === "loading") {
24660
+ children.push(React.createElement(ui.Text, { key: "loading", color: "subtle" }, "Creating authorization QR code\u2026"));
24661
+ } else if (snapshot.phase === "waiting" && snapshot.qr !== void 0 && snapshot.scanUrl !== void 0) {
24662
+ const fullRows = snapshot.qr.split("\n").length;
24663
+ const fullColumns = printableWidth(snapshot.qr.split("\n")[0] ?? "");
24664
+ const useFullQr = terminal.rows >= fullRows + 7 && terminal.columns >= fullColumns + 4;
24665
+ const qr = useFullQr || snapshot.compactQr === void 0 ? snapshot.qr : snapshot.compactQr;
24666
+ const qrRows = qr.split("\n").length;
24667
+ const qrColumns = printableWidth(qr.split("\n")[0] ?? "");
24668
+ if (terminal.rows < qrRows + 7 || terminal.columns < qrColumns + 4) {
24669
+ children.push(React.createElement(
24670
+ ui.Text,
24671
+ { key: "size-warning", color: "warning" },
24672
+ `Resize the terminal to at least ${qrColumns + 4} \xD7 ${qrRows + 7} if the QR code is clipped.`
24673
+ ));
24674
+ }
24675
+ children.push(
24676
+ React.createElement(ui.Text, { key: "scan" }, "Scan this QR code to authorize this Host:"),
24677
+ React.createElement(ui.Text, { key: "qr", wrap: "truncate" }, qr),
24678
+ React.createElement(ui.Text, { key: "url", color: "link", underline: true }, terminalLink2(snapshot.scanUrl)),
24679
+ React.createElement(ui.Text, { key: "waiting", color: "subtle" }, "Waiting for authorization\u2026")
24680
+ );
24681
+ } else if (snapshot.phase === "complete") {
24682
+ children.push(React.createElement(
24683
+ ui.Text,
24684
+ { key: "complete", color: "success" },
24685
+ snapshot.account === void 0 ? "Remote Host login complete. The Host is reconnecting now." : `Remote Host login complete for ${snapshot.account}. The Host is reconnecting now.`
24686
+ ));
24687
+ } else if (snapshot.phase === "expired") {
24688
+ children.push(React.createElement(ui.Text, { key: "expired", color: "warning" }, "The QR code expired. Close this screen and run /remote login again."));
24689
+ } else if (snapshot.phase === "error") {
24690
+ children.push(React.createElement(ui.Text, { key: "error", color: "error" }, `Remote Host login failed (${snapshot.error ?? "UNKNOWN"}).`));
24691
+ }
24692
+ children.push(React.createElement(ui.Text, { key: "close", color: "subtle" }, "Press Esc or q to return."));
24693
+ return React.createElement(ui.Box, {
24694
+ flexDirection: "column",
24695
+ gap: 1,
24696
+ paddingX: Math.max(0, Math.min(2, Math.floor((terminal.columns - 1) / 2)))
24697
+ }, ...children);
24698
+ };
24699
+ }
24700
+ function createRemoteStatusScene(resolveTarget) {
24701
+ return function RemoteStatusScene({ React, ui, close }) {
24702
+ const terminal = ui.useTerminalSize();
24703
+ ui.useInput((input2, key) => {
24704
+ if (key.escape === true || input2.toLowerCase() === "q") close();
24705
+ });
24706
+ const children = [
24707
+ React.createElement(ui.Text, { key: "title", bold: true, color: "accent" }, "Remote Host status"),
24708
+ ...remoteStatusLines(resolveTarget()).map((line, index) => React.createElement(
24709
+ ui.Text,
24710
+ { key: `status-${index}`, color: line === "" ? "subtle" : void 0 },
24711
+ line === "" ? " " : line
24712
+ )),
24713
+ React.createElement(ui.Text, { key: "close", color: "subtle" }, "Press Esc or q to return.")
24714
+ ];
24715
+ return React.createElement(ui.Box, {
24716
+ flexDirection: "column",
24717
+ gap: 1,
24718
+ paddingX: Math.max(0, Math.min(2, Math.floor((terminal.columns - 1) / 2)))
24719
+ }, ...children);
24720
+ };
24721
+ }
24722
+ function terminalLink2(url) {
24723
+ return `\x1B]8;;${url}\x07${url}\x1B]8;;\x07`;
24724
+ }
24725
+ function printableWidth(value) {
24726
+ return value.replace(/\u001B\[[0-9;]*m/gu, "").length;
24727
+ }
24728
+ function errorCode4(error) {
24729
+ if (error instanceof ServerApiError) return error.code;
24730
+ if (error instanceof Error && "code" in error && typeof error.code === "string") return error.code;
24731
+ return "CONNECTION_FAILED";
24732
+ }
24733
+ function wait(milliseconds) {
24734
+ return new Promise((resolve3) => setTimeout(resolve3, milliseconds));
24735
+ }
24736
+
23860
24737
  // src/remote-file-content-provider.ts
23861
24738
  var REMOTE_FILE_SAVE_AS_MAX_BYTES = 100 * 1024 * 1024;
23862
24739
  var REMOTE_FILE_FAST_SAVE_AS_MAX_BYTES = 1024 * 1024 * 1024;
@@ -23936,15 +24813,18 @@ var legacyLoaderModuleNames = /* @__PURE__ */ new Set(["dsh-remote", "@dsh-remot
23936
24813
  var pluginSettingsNamespace = "ds-harness-remote";
23937
24814
  var legacySettingsNamespace = "dsh-remote";
23938
24815
  function apply(ctx, input2 = {}) {
23939
- ctx.inject(["settings", "connection", "typertGateway"], (runtimeContext) => {
24816
+ const tuiCommandsAvailable = ctx.get("commands", false) !== void 0 && ctx.get("tuiScenes", false) !== void 0;
24817
+ const tuiBinding = tuiCommandsAvailable ? {} : void 0;
24818
+ if (tuiBinding !== void 0) installTuiRemoteCommand(ctx, () => tuiBinding.target);
24819
+ ctx.inject(["settings", "typertGateway"], (runtimeContext) => {
23940
24820
  const gateway = runtimeContext.get("typertGateway");
23941
- if (runtimeContext.get("apiProxy") !== void 0 || new TypertGatewaySwitch(gateway).supportsCarrier()) {
23942
- return activate(runtimeContext, input2);
24821
+ if (tuiCommandsAvailable || runtimeContext.get("apiProxy") !== void 0 || new TypertGatewaySwitch(gateway).supportsCarrier()) {
24822
+ return activate(runtimeContext, input2, tuiBinding);
23943
24823
  }
23944
24824
  runtimeContext.inject(["apiProxy"], (legacyContext) => activate(legacyContext, input2));
23945
24825
  });
23946
24826
  }
23947
- async function activate(ctx, input2) {
24827
+ async function activate(ctx, input2, tuiBinding) {
23948
24828
  const settings = ctx.get("settings");
23949
24829
  const settingsScope = settings?.register(pluginSettingsNamespace, Config, {
23950
24830
  base: input2,
@@ -23954,11 +24834,12 @@ async function activate(ctx, input2) {
23954
24834
  }
23955
24835
  });
23956
24836
  if (settings !== void 0 && settingsScope !== void 0) {
23957
- const migration = await migrateLegacySettings(settings, settingsScope);
23958
- if (migration === "migrated") ctx.logger.info("migrated legacy Remote settings namespace");
23959
- if (migration === "failed") ctx.logger.warn("failed to migrate legacy Remote settings namespace");
24837
+ const migration = migrateLegacySettings(settings, settingsScope);
24838
+ reportSettingsMigration(ctx, await migration);
23960
24839
  }
23961
- const config = resolveConfig(settingsScope?.get() ?? input2);
24840
+ const connection = ctx.get("connection");
24841
+ const resolvedConfig = resolveConfig(settingsScope?.get() ?? input2);
24842
+ const config = connection === void 0 && resolvedConfig.serverUrl === void 0 ? { ...resolvedConfig, serverUrl: DEFAULT_REMOTE_SERVER_URL } : resolvedConfig;
23962
24843
  if (!config.enabled) return;
23963
24844
  const logger = new SafeLogger({
23964
24845
  debug: (message) => {
@@ -23978,13 +24859,11 @@ async function activate(ctx, input2) {
23978
24859
  console.error(message);
23979
24860
  }
23980
24861
  }, config.logLevel);
23981
- await disableLegacyLoaderEntries(ctx, logger);
23982
24862
  const defaultIdentityDirectory = new IdentityStore().directory;
23983
24863
  const hostIdentities = new IdentityStore({
23984
24864
  directory: config.serverUrl === void 0 ? defaultIdentityDirectory : serverStorageDirectory(defaultIdentityDirectory, config.serverUrl, "host")
23985
24865
  });
23986
24866
  const apiProxy = ctx.get("apiProxy");
23987
- const connection = ctx.get("connection");
23988
24867
  const nativeTypertGateway = ctx.get("typertGateway");
23989
24868
  const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway).local();
23990
24869
  const runtime = new HostPluginRuntime(
@@ -24014,6 +24893,9 @@ async function activate(ctx, input2) {
24014
24893
  const controlRuntime = connection === void 0 ? void 0 : new PluginControlRuntime(config, defaultIdentityDirectory, settingsScope, clientRuntime, hostControl);
24015
24894
  ctx.provide("dshRemote", runtime);
24016
24895
  if (clientRuntime !== void 0) ctx.provide("dshRemoteClient", clientRuntime);
24896
+ const tuiTarget = { runtime, config };
24897
+ if (tuiBinding !== void 0) tuiBinding.target = tuiTarget;
24898
+ await disableLegacyLoaderEntries(ctx, logger);
24017
24899
  await ctx.effect(async () => {
24018
24900
  const disposeControl = controlRuntime?.register(connection);
24019
24901
  try {
@@ -24029,18 +24911,24 @@ async function activate(ctx, input2) {
24029
24911
  });
24030
24912
  }
24031
24913
  } catch (error) {
24914
+ if (tuiBinding?.target === tuiTarget) tuiBinding.target = void 0;
24032
24915
  await disposeControl?.();
24033
24916
  await clientRuntime?.close();
24034
24917
  await runtime.close();
24035
24918
  throw error;
24036
24919
  }
24037
24920
  return async () => {
24921
+ if (tuiBinding?.target === tuiTarget) tuiBinding.target = void 0;
24038
24922
  await disposeControl?.();
24039
24923
  await clientRuntime?.close();
24040
24924
  await runtime.close();
24041
24925
  };
24042
24926
  }, "dsh-remote lifecycle");
24043
24927
  }
24928
+ function reportSettingsMigration(ctx, migration) {
24929
+ if (migration === "migrated") ctx.logger.info("migrated legacy Remote settings namespace");
24930
+ if (migration === "failed") ctx.logger.warn("failed to migrate legacy Remote settings namespace");
24931
+ }
24044
24932
  async function migrateLegacySettings(settings, currentScope) {
24045
24933
  if (typeof settings.describe !== "function") return "skipped";
24046
24934
  try {
@@ -24131,6 +25019,7 @@ export {
24131
25019
  migrateLegacySettings,
24132
25020
  name,
24133
25021
  resolveConfig,
25022
+ runCli,
24134
25023
  serverStorageDirectory
24135
25024
  };
24136
25025
  /*! Bundled license information: