@kitman-sh/cli 0.10.74 → 0.10.75

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.
Files changed (26) hide show
  1. package/dist/{chunk-kd02wvfa.js → chunk-0e3t90fs.js} +8 -8
  2. package/dist/{chunk-x0ep0a1y.js → chunk-1zt1yeme.js} +4 -4
  3. package/dist/{chunk-ggax0n15.js → chunk-3teaa3qx.js} +3 -3
  4. package/dist/{chunk-rrnee9mk.js → chunk-545b129z.js} +38 -5
  5. package/dist/{chunk-q3c7vmby.js → chunk-76ctrqm6.js} +1 -1
  6. package/dist/{chunk-rt94p780.js → chunk-89547s8p.js} +647 -75
  7. package/dist/{chunk-1rry0tsn.js → chunk-98sg1yyd.js} +2 -2
  8. package/dist/{chunk-60333x3q.js → chunk-9zd52pda.js} +5 -5
  9. package/dist/{chunk-3edjgnxf.js → chunk-b0cscqbw.js} +25 -3
  10. package/dist/{chunk-g4fvd8r1.js → chunk-bcbne0ek.js} +22 -6
  11. package/dist/{chunk-8bhea26e.js → chunk-e3dmkypq.js} +5 -5
  12. package/dist/{chunk-csbf77zs.js → chunk-e8zm8z3a.js} +4 -4
  13. package/dist/{chunk-3ne401fy.js → chunk-f0stxany.js} +7 -7
  14. package/dist/{chunk-rm83dbpv.js → chunk-fq47a5yd.js} +2 -2
  15. package/dist/{chunk-2v7pbpfw.js → chunk-fqhxxxmp.js} +3 -3
  16. package/dist/{chunk-mzphfkyk.js → chunk-h3y4z2c0.js} +7 -7
  17. package/dist/{chunk-yzm5s3ng.js → chunk-jb01khjp.js} +4 -4
  18. package/dist/{chunk-d3ksv8y1.js → chunk-jbwk6k6m.js} +44 -7
  19. package/dist/{chunk-xz1z7j2d.js → chunk-p8g8qyev.js} +4 -4
  20. package/dist/{chunk-5rs3yv84.js → chunk-qs6h506g.js} +15 -15
  21. package/dist/{chunk-gmnqchsc.js → chunk-t07tn0ay.js} +4 -4
  22. package/dist/{chunk-mv288r6q.js → chunk-tv3fkqb9.js} +10 -10
  23. package/dist/{chunk-v01c0k6j.js → chunk-tzz4fv17.js} +1 -1
  24. package/dist/{chunk-dk0ftqwg.js → chunk-xb70ys3n.js} +1 -1
  25. package/dist/index.js +4 -4
  26. package/package.json +1 -1
@@ -1,11 +1,12 @@
1
1
  // @bun
2
2
  import {
3
3
  toJsonValue,
4
+ isKeyOf,
4
5
  isNumber,
5
6
  isObject,
6
7
  isJsonObject,
7
8
  isString
8
- } from "./chunk-v01c0k6j.js";
9
+ } from "./chunk-tzz4fv17.js";
9
10
  import {
10
11
  pipeArguments,
11
12
  dual,
@@ -19,7 +20,10 @@ import {
19
20
  match,
20
21
  getOrElse,
21
22
  getOrUndefined,
23
+ liftThrowable,
22
24
  flatMap,
25
+ toArray,
26
+ filter2,
23
27
  Service2,
24
28
  Reference,
25
29
  fromInputUnsafe,
@@ -50,6 +54,7 @@ import {
50
54
  doneUnsafe,
51
55
  makeUnsafe6,
52
56
  provide,
57
+ succeed5,
53
58
  effect,
54
59
  mergeAll,
55
60
  die2,
@@ -104,7 +109,7 @@ import {
104
109
  sleep,
105
110
  raceAllFirst,
106
111
  raceFirst,
107
- filter2,
112
+ filter3,
108
113
  matchEffect2,
109
114
  context,
110
115
  provide3,
@@ -136,31 +141,36 @@ import {
136
141
  String4,
137
142
  Boolean2,
138
143
  Struct,
144
+ Record,
139
145
  ArraySchema,
140
146
  Union,
141
147
  Literals,
142
148
  NullOr,
143
149
  isPattern,
144
150
  Finite,
151
+ isGreaterThan,
145
152
  isGreaterThanOrEqualTo,
146
153
  isLessThanOrEqualTo,
147
154
  Int,
148
155
  isMinLength,
149
156
  isMaxLength,
157
+ NonEmptyString,
150
158
  Date2,
151
159
  fromJsonString,
152
160
  TaggedError2,
161
+ Json,
153
162
  decodeJsonString,
154
163
  decodeJsonStringOption,
155
164
  encodeJsonStringSync,
156
165
  encodePrettyJsonString,
157
166
  decodeJsonStringSync,
158
167
  encodeUnknownJsonStringSync
159
- } from "./chunk-d3ksv8y1.js";
168
+ } from "./chunk-jbwk6k6m.js";
160
169
  import {
161
170
  Id,
162
- secretReference
163
- } from "./chunk-rrnee9mk.js";
171
+ secretReference,
172
+ secretPlaceholder
173
+ } from "./chunk-545b129z.js";
164
174
  import {
165
175
  currentTimeMillis,
166
176
  SecureRandom,
@@ -190,9 +200,15 @@ import {
190
200
  digestSha256,
191
201
  AgentEvent,
192
202
  get,
203
+ post,
193
204
  setHeaders,
205
+ acceptJson,
206
+ bodyJsonUnsafe,
207
+ bodyUrlParams,
208
+ schemaBodyJson,
194
209
  filterStatusOk,
195
210
  HttpClient,
211
+ layer4,
196
212
  SetupArchive,
197
213
  kitMcpSecretNames,
198
214
  RunSettled,
@@ -227,7 +243,7 @@ import {
227
243
  installCommand,
228
244
  checkServicesFile,
229
245
  checkServices
230
- } from "./chunk-g4fvd8r1.js";
246
+ } from "./chunk-bcbne0ek.js";
231
247
  import {
232
248
  join,
233
249
  interrupt,
@@ -267,7 +283,7 @@ import {
267
283
  writeSrtSettings2,
268
284
  srtStepArgv2,
269
285
  srtArgv2
270
- } from "./chunk-dk0ftqwg.js";
286
+ } from "./chunk-xb70ys3n.js";
271
287
  import {
272
288
  writeAgentEnvironmentConfigFile2,
273
289
  readManifestSkillPaths2,
@@ -283,8 +299,8 @@ import {
283
299
  applyAgentEnvironmentMaterialization2,
284
300
  isPathWithin2,
285
301
  skillIdentity2
286
- } from "./chunk-8bhea26e.js";
287
- import"./chunk-2v7pbpfw.js";
302
+ } from "./chunk-e3dmkypq.js";
303
+ import"./chunk-fqhxxxmp.js";
288
304
  import {
289
305
  egressAllowed,
290
306
  writeSecretRegistry,
@@ -294,7 +310,7 @@ import {
294
310
  secretFilter2,
295
311
  opaqueTunnelRefusal2,
296
312
  isOpaqueConnectReason2
297
- } from "./chunk-3edjgnxf.js";
313
+ } from "./chunk-b0cscqbw.js";
298
314
  import {
299
315
  sliceBytes2,
300
316
  maxLineBytes2,
@@ -302,23 +318,23 @@ import {
302
318
  decodedOption2,
303
319
  decoded2,
304
320
  readOpencode2
305
- } from "./chunk-csbf77zs.js";
321
+ } from "./chunk-e8zm8z3a.js";
306
322
  import {
307
323
  uploadRunFile,
308
324
  programLease,
309
325
  rpcRunFiles,
310
326
  programHostsEnv,
311
327
  undeclaredHostsEnv
312
- } from "./chunk-yzm5s3ng.js";
313
- import"./chunk-xz1z7j2d.js";
328
+ } from "./chunk-jb01khjp.js";
329
+ import"./chunk-p8g8qyev.js";
314
330
  import {
315
331
  nextInt
316
- } from "./chunk-q3c7vmby.js";
332
+ } from "./chunk-76ctrqm6.js";
317
333
  import {
318
334
  ChildProcessSpawner,
319
335
  make3
320
- } from "./chunk-1rry0tsn.js";
321
- import"./chunk-ggax0n15.js";
336
+ } from "./chunk-98sg1yyd.js";
337
+ import"./chunk-3teaa3qx.js";
322
338
 
323
339
  // packages/core/src/sync-kit.ts
324
340
  import { createHash } from "crypto";
@@ -1614,6 +1630,8 @@ function repositoryUrl(repoBinding) {
1614
1630
  }
1615
1631
  // packages/agent-runtime/src/harness-adapters.ts
1616
1632
  import { homedir } from "os";
1633
+ var anthropicHosts = ["api.anthropic.com", "console.anthropic.com", "claude.ai"];
1634
+ var githubCopilotHosts = ["api.githubcopilot.com", "*.githubcopilot.com"];
1617
1635
  var HarnessAdapters = [
1618
1636
  {
1619
1637
  id: "claude",
@@ -1628,10 +1646,20 @@ var HarnessAdapters = [
1628
1646
  file: ".credentials.json",
1629
1647
  variable: "CLAUDE_SECURESTORAGE_CONFIG_DIR",
1630
1648
  format: "claude",
1649
+ referenceName: "claude-code-login",
1631
1650
  refreshArgs: ["-p", "/usage", "--setting-sources", "", "--no-session-persistence"],
1632
1651
  renewWithin: "4 minutes"
1633
1652
  },
1634
- providerHosts: ["api.anthropic.com", "console.anthropic.com", "claude.ai"],
1653
+ providerHosts: anthropicHosts,
1654
+ loginHosts: { claudeAiOauth: anthropicHosts },
1655
+ globalConfig: {
1656
+ file: ".claude.json",
1657
+ variable: "CLAUDE_CONFIG_DIR",
1658
+ keyField: "primaryApiKey",
1659
+ hosts: ["api.anthropic.com"],
1660
+ referenceName: "claude-api-key",
1661
+ links: { projects: ".claude/projects" }
1662
+ },
1635
1663
  effortArgs: ["--effort", "{effort}"],
1636
1664
  autoPermissionArgs: ["--dangerously-skip-permissions"],
1637
1665
  requiredArgs: [
@@ -1710,6 +1738,19 @@ var HarnessAdapters = [
1710
1738
  "generativelanguage.googleapis.com",
1711
1739
  "openrouter.ai"
1712
1740
  ],
1741
+ loginHosts: {
1742
+ anthropic: ["api.anthropic.com"],
1743
+ openai: ["api.openai.com"],
1744
+ "openai-codex": ["chatgpt.com"],
1745
+ google: ["generativelanguage.googleapis.com"],
1746
+ "google-gemini-cli": ["cloudcode-pa.googleapis.com"],
1747
+ "github-copilot": githubCopilotHosts,
1748
+ openrouter: ["openrouter.ai"],
1749
+ groq: ["api.groq.com"],
1750
+ xai: ["api.x.ai"],
1751
+ mistral: ["api.mistral.ai"],
1752
+ cerebras: ["api.cerebras.ai"]
1753
+ },
1713
1754
  effortArgs: ["--thinking", "{effort}"],
1714
1755
  autoPermissionArgs: ["--no-approve"],
1715
1756
  requiredArgs: ["--no-skills"],
@@ -1721,7 +1762,8 @@ var HarnessAdapters = [
1721
1762
  variable: "PI_CODING_AGENT_DIR",
1722
1763
  sessionsVariable: "PI_CODING_AGENT_SESSION_DIR",
1723
1764
  operatorDir: ".pi/agent",
1724
- copies: ["models.json", "models-store.json"],
1765
+ copies: ["models-store.json"],
1766
+ providers: "models.json",
1725
1767
  login: {
1726
1768
  file: "auth.json",
1727
1769
  refreshArgs: ["auth", "print-bearer-token", "--min-expiry", "15m", "--provider", "{provider}"],
@@ -1754,6 +1796,19 @@ var HarnessAdapters = [
1754
1796
  "opencode.ai",
1755
1797
  "models.dev"
1756
1798
  ],
1799
+ loginHosts: {
1800
+ anthropic: anthropicHosts,
1801
+ openai: ["api.openai.com", "auth.openai.com", "chatgpt.com"],
1802
+ opencode: ["opencode.ai"],
1803
+ google: ["generativelanguage.googleapis.com"],
1804
+ "github-copilot": githubCopilotHosts,
1805
+ openrouter: ["openrouter.ai"],
1806
+ groq: ["api.groq.com"],
1807
+ xai: ["api.x.ai"],
1808
+ mistral: ["api.mistral.ai"],
1809
+ cerebras: ["api.cerebras.ai"]
1810
+ },
1811
+ loginRefreshHosts: { "github-copilot": [...githubCopilotHosts, "api.github.com"] },
1757
1812
  autoPermissionArgs: ["--auto"],
1758
1813
  flagWhereListed: "--standalone",
1759
1814
  mcp: { kind: "config_env", variable: "OPENCODE_CONFIG_CONTENT" },
@@ -1783,6 +1838,12 @@ function kitmanMcpToolTimeout(adapter, seconds) {
1783
1838
  const value = timeout.unit === "ms" ? seconds * 1000 : seconds;
1784
1839
  return timeout.kind === "env" ? { env: Object.fromEntries(timeout.variables.map((variable) => [variable, String(value)])) } : { server: { [timeout.field]: value } };
1785
1840
  }
1841
+ var harnessNetworkHosts = (adapter) => [
1842
+ ...new Set([
1843
+ ...adapter?.providerHosts ?? [],
1844
+ ...[adapter?.loginHosts, adapter?.loginRefreshHosts].flatMap((hosts) => Object.values(hosts ?? {}).flat())
1845
+ ])
1846
+ ];
1786
1847
  var providerKeyHosts = new Map([
1787
1848
  ["ANTHROPIC_API_KEY", ["api.anthropic.com"]],
1788
1849
  ["ANTHROPIC_AUTH_TOKEN", ["api.anthropic.com", "console.anthropic.com", "claude.ai"]],
@@ -1803,18 +1864,53 @@ function resolveHarnessLaunch(input) {
1803
1864
  const launch = input.adapter.env === undefined ? kit : { ...kit, env: { ...input.adapter.env, ...kit.env } };
1804
1865
  const effort = input.effort;
1805
1866
  const effortful = effort === undefined || input.adapter.effortArgs === undefined ? launch : { ...launch, args: [...launch.args, ...input.adapter.effortArgs.map((arg) => arg.replace("{effort}", effort))] };
1806
- return flagProbedLaunch(input.adapter, sandboxHomeLaunch(input, effortful));
1807
- }
1808
- var sandboxedLaunch = (launch, backend) => launch.sandboxed === undefined ? launch : {
1809
- ...launch,
1810
- env: {
1811
- ...launch.env,
1812
- ...launch.sandboxed.env,
1813
- ...backend === "srt" ? { XDG_CONFIG_HOME: launch.sandboxed.configHome } : undefined
1814
- },
1815
- loginCopy: launch.sandboxed.login,
1816
- homeDirectory: launch.sandboxed.homeDirectory
1867
+ return flagProbedLaunch(input.adapter, globalConfigLaunch(input, sandboxHomeLaunch(input, effortful)));
1868
+ }
1869
+ var sandboxedLaunch = (launch, backend) => {
1870
+ const sandboxed = launch.sandboxed;
1871
+ if (sandboxed === undefined)
1872
+ return launch;
1873
+ const srt = backend === "srt";
1874
+ const config = srt ? sandboxed.globalConfig : undefined;
1875
+ return {
1876
+ ...launch,
1877
+ env: {
1878
+ ...launch.env,
1879
+ ...sandboxed.env,
1880
+ ...srt && sandboxed.configHome !== undefined ? { XDG_CONFIG_HOME: sandboxed.configHome } : undefined,
1881
+ ...config === undefined ? undefined : config.env
1882
+ },
1883
+ ...sandboxed.login === undefined ? undefined : { renewedLogin: sandboxed.login },
1884
+ ...sandboxed.homeDirectory === undefined ? undefined : { homeDirectory: sandboxed.homeDirectory },
1885
+ ...config === undefined ? undefined : { globalConfig: config }
1886
+ };
1817
1887
  };
1888
+ function globalConfigLaunch(input, launch) {
1889
+ const config = input.adapter.globalConfig;
1890
+ if (config === undefined)
1891
+ return launch;
1892
+ const home = input.home ?? homedir();
1893
+ const directory = `${input.configDir}/global-config`;
1894
+ return {
1895
+ ...launch,
1896
+ sandboxed: {
1897
+ env: {},
1898
+ ...launch.sandboxed,
1899
+ globalConfig: {
1900
+ env: { [config.variable]: directory },
1901
+ path: `${directory}/${config.file}`,
1902
+ source: `${home}/${config.file}`,
1903
+ keyField: config.keyField,
1904
+ hosts: config.hosts,
1905
+ referenceName: config.referenceName,
1906
+ links: Object.entries(config.links).map(([name, target]) => ({
1907
+ path: `${directory}/${name}`,
1908
+ target: `${home}/${target}`
1909
+ }))
1910
+ }
1911
+ }
1912
+ };
1913
+ }
1818
1914
  function sandboxHomeLaunch(input, launch) {
1819
1915
  const { sandboxHome } = input.adapter;
1820
1916
  if (sandboxHome === undefined)
@@ -1828,7 +1924,13 @@ function sandboxHomeLaunch(input, launch) {
1828
1924
  configHome,
1829
1925
  login: {
1830
1926
  path: `${input.configDir}/${sandboxHome.login.path}`,
1831
- source: `${home}/${sandboxHome.login.source}`
1927
+ source: `${home}/${sandboxHome.login.source}`,
1928
+ format: "opencode",
1929
+ binary: input.adapter.binary,
1930
+ hosts: input.adapter.loginHosts ?? {},
1931
+ refreshTokenHosts: input.adapter.loginRefreshHosts ?? {},
1932
+ referenceName: `${input.adapter.id}-login`,
1933
+ refresh: { args: [], env: {}, within: "10 minutes" }
1832
1934
  },
1833
1935
  homeDirectory: `${configHome}/${sandboxHome.configHomeDirectory}`
1834
1936
  }
@@ -1868,6 +1970,9 @@ function agentDirLaunch(input, launch) {
1868
1970
  path: `${directory}/${agentDir.login.file}`,
1869
1971
  source: `${operator}/${agentDir.login.file}`,
1870
1972
  format: "pi",
1973
+ binary: input.adapter.binary,
1974
+ hosts: input.adapter.loginHosts ?? {},
1975
+ referenceName: `${input.adapter.id}-login`,
1871
1976
  refresh: {
1872
1977
  args: agentDir.login.refreshArgs,
1873
1978
  env: { [agentDir.variable]: operator },
@@ -1878,6 +1983,14 @@ function agentDirLaunch(input, launch) {
1878
1983
  path: `${directory}/${agentDir.settings.file}`,
1879
1984
  source: `${operator}/${agentDir.settings.file}`,
1880
1985
  keys: agentDir.settings.keys
1986
+ },
1987
+ ...agentDir.providers === undefined ? undefined : {
1988
+ providers: {
1989
+ path: `${directory}/${agentDir.providers}`,
1990
+ source: `${operator}/${agentDir.providers}`,
1991
+ hosts: input.adapter.loginHosts ?? {},
1992
+ referenceName: `${input.adapter.id}-provider`
1993
+ }
1881
1994
  }
1882
1995
  }
1883
1996
  };
@@ -1894,6 +2007,9 @@ function renewedLoginLaunch(input, launch) {
1894
2007
  path: directory,
1895
2008
  source: `${input.home ?? homedir()}/${login.operatorDir}/${login.file}`,
1896
2009
  format: login.format,
2010
+ binary: input.adapter.binary,
2011
+ hosts: input.adapter.loginHosts ?? {},
2012
+ referenceName: login.referenceName,
1897
2013
  linksDirectory: true,
1898
2014
  refresh: { args: login.refreshArgs, env: {}, within: login.renewWithin }
1899
2015
  }
@@ -2100,6 +2216,189 @@ function mcpLaunch(input, skillsDir) {
2100
2216
  };
2101
2217
  }
2102
2218
  }
2219
+ // packages/agent-runtime/src/opencode-login.ts
2220
+ class OpenCodeLoginNotRenewed extends TaggedError("OpenCodeLoginNotRenewed") {
2221
+ get message() {
2222
+ return `OpenCode's ${this.provider} login was not renewed: ${this.reason}`;
2223
+ }
2224
+ }
2225
+ var StoredCredential = Struct({
2226
+ id: String4,
2227
+ integrationID: String4,
2228
+ label: optionalKey(NullOr(String4)),
2229
+ value: Record(String4, Json),
2230
+ active: optionalKey(Boolean2)
2231
+ });
2232
+ var decodeExport = decodeUnknownOption(fromJsonString(ArraySchema(Json)));
2233
+ var decodeStored = decodeUnknownOption(StoredCredential);
2234
+ var RenewableLogin = Struct({
2235
+ type: Literal("oauth"),
2236
+ access: String4,
2237
+ refresh: NonEmptyString,
2238
+ expires: Finite.check(isGreaterThan(0))
2239
+ });
2240
+ var decodeRenewable = decodeUnknownOption(RenewableLogin);
2241
+ var TokenAnswer = Struct({
2242
+ access_token: String4,
2243
+ refresh_token: optionalKey(String4),
2244
+ expires_in: Finite
2245
+ });
2246
+ var XaiAnswer = Struct({
2247
+ access_token: String4,
2248
+ refresh_token: optionalKey(String4),
2249
+ expires_in: optionalKey(Finite)
2250
+ });
2251
+ var JwtExpiry = fromJsonString(Struct({ exp: Finite }));
2252
+ var decodeJwtExpiry = decodeUnknownOption(JwtExpiry);
2253
+ var CopilotAnswer = Struct({ token: String4, expires_at: Finite });
2254
+ var legacyCredential = (value) => {
2255
+ const metadata = isJsonObject(value.metadata) ? value.metadata : {};
2256
+ const strings = Object.fromEntries(Object.entries(metadata).filter(([, entry]) => isString(entry)));
2257
+ const carried = Object.keys(strings).length > 0 ? { metadata: strings } : undefined;
2258
+ if (value.type === "oauth")
2259
+ return Object.assign({ type: "oauth", refresh: value.refresh ?? "", access: value.access ?? "", expires: value.expires ?? 0 }, isString(metadata.accountID) ? { accountId: metadata.accountID } : undefined, isString(metadata.enterpriseUrl) ? { enterpriseUrl: metadata.enterpriseUrl } : undefined, carried);
2260
+ return value.type === "key" && isString(value.key) ? Object.assign({ type: "api", key: value.key }, carried) : undefined;
2261
+ };
2262
+ var activeCredentials = (entries) => {
2263
+ const chosen = {};
2264
+ for (const entry of entries)
2265
+ if (chosen[entry.integrationID] === undefined || entry.active === true && chosen[entry.integrationID]?.active !== true)
2266
+ chosen[entry.integrationID] = entry;
2267
+ return chosen;
2268
+ };
2269
+ var fileCredentials = (source) => gen(function* () {
2270
+ const text = yield* (yield* FileSystem).readFileString(source).pipe(orElseSucceed(() => ""));
2271
+ const credentials = getOrUndefined(decodeJsonStringOption(text));
2272
+ return isJsonObject(credentials) ? credentials : undefined;
2273
+ });
2274
+ var openCodeStore = (source) => gen(function* () {
2275
+ const path = yield* Path;
2276
+ const store = path.join(path.dirname(source), "opencode.db");
2277
+ const exists = yield* (yield* FileSystem).exists(store).pipe(orElseSucceed(() => false));
2278
+ return exists ? { env: { XDG_DATA_HOME: path.dirname(path.dirname(source)) } } : undefined;
2279
+ });
2280
+ var runOpenCode = (binary, store, args) => runCommand2(binary, [...args, "--standalone"], { env: store.env, timeout: "30 seconds" });
2281
+ var readOpenCodeLogin = (binary, source) => gen(function* () {
2282
+ const store = yield* openCodeStore(source);
2283
+ const exported = store === undefined ? none() : yield* runOpenCode(binary, store, ["auth", "export"]).pipe(map8(decodeExport), orElseSucceed(() => none()));
2284
+ if (isNone(exported)) {
2285
+ const credentials = yield* fileCredentials(source);
2286
+ return credentials === undefined ? undefined : { credentials };
2287
+ }
2288
+ const stored = activeCredentials(exported.value.flatMap((entry) => toArray(decodeStored(entry))));
2289
+ const credentials = Object.fromEntries(Object.entries(stored).flatMap(([provider, entry]) => {
2290
+ const legacy = legacyCredential(entry.value);
2291
+ return legacy === undefined ? [] : [[provider, legacy]];
2292
+ }));
2293
+ return { credentials, stored };
2294
+ });
2295
+ var answer = (provider, schema, request) => gen(function* () {
2296
+ const response = yield* (yield* HttpClient).execute(request);
2297
+ const ok = yield* filterStatusOk(response);
2298
+ return yield* schemaBodyJson(schema)(ok);
2299
+ }).pipe(mapError2((error) => new OpenCodeLoginNotRenewed({ provider, reason: error.message })));
2300
+ var refreshed = (now) => (answered) => ({
2301
+ access: answered.access_token,
2302
+ ...answered.refresh_token === undefined ? undefined : { refresh: answered.refresh_token },
2303
+ expires: now + answered.expires_in * 1000
2304
+ });
2305
+ var xaiExpiry = (answered, now) => {
2306
+ if (answered.expires_in !== undefined && answered.expires_in > 0)
2307
+ return now + answered.expires_in * 1000;
2308
+ const payload = answered.access_token.split(".")[1];
2309
+ const exp = payload === undefined ? none() : decodeJwtExpiry(Buffer.from(payload, "base64url").toString());
2310
+ return match(exp, { onNone: () => now + 3600000, onSome: ({ exp }) => exp * 1000 });
2311
+ };
2312
+ var openCodeConsole = "https://opencode.ai/console";
2313
+ var consoleServer = (metadata) => (isString(metadata.server) ? metadata.server : openCodeConsole).replace(/^https:\/\/console\.opencode\.ai(?=\/|$)/, openCodeConsole);
2314
+ var consoleMetadata = (credential) => isJsonObject(credential) && isJsonObject(credential.metadata) ? credential.metadata : {};
2315
+ var openCodeConsoleHosts = (credentials) => {
2316
+ if (credentials?.opencode === undefined)
2317
+ return [];
2318
+ const server = liftThrowable(() => new URL(consoleServer(consoleMetadata(credentials.opencode))))();
2319
+ return match(filter2(server, (url) => url.protocol === "https:"), {
2320
+ onNone: () => [],
2321
+ onSome: (url) => [url.host]
2322
+ });
2323
+ };
2324
+ var openCodeConsoleKey = (credential) => Object.assign({ type: "api", key: credential.access ?? "" }, isJsonObject(credential.metadata) ? { metadata: credential.metadata } : undefined);
2325
+ var renewers = {
2326
+ anthropic: (login, now) => answer("anthropic", TokenAnswer, post("https://platform.claude.com/v1/oauth/token").pipe(bodyJsonUnsafe({
2327
+ grant_type: "refresh_token",
2328
+ refresh_token: login.refresh,
2329
+ client_id: "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
2330
+ }))).pipe(map8(refreshed(now))),
2331
+ openai: (login, now) => answer("openai", TokenAnswer, post("https://auth.openai.com/oauth/token").pipe(bodyUrlParams({
2332
+ grant_type: "refresh_token",
2333
+ refresh_token: login.refresh,
2334
+ client_id: "app_EMoamEEZ73f0CkXaXp7hrann"
2335
+ }))).pipe(map8(refreshed(now))),
2336
+ xai: (login, now) => answer("xai", XaiAnswer, post("https://auth.x.ai/oauth2/token").pipe(acceptJson, bodyUrlParams({
2337
+ grant_type: "refresh_token",
2338
+ refresh_token: login.refresh,
2339
+ client_id: "b1a00492-073a-47ea-816f-4c329264a828"
2340
+ }))).pipe(map8((answered) => ({
2341
+ access: answered.access_token,
2342
+ ...answered.refresh_token === undefined ? undefined : { refresh: answered.refresh_token },
2343
+ expires: xaiExpiry(answered, now)
2344
+ }))),
2345
+ opencode: (login, now, metadata) => answer("opencode", TokenAnswer, post(`${consoleServer(metadata)}/auth/device/token`).pipe(acceptJson, bodyJsonUnsafe({
2346
+ grant_type: "refresh_token",
2347
+ refresh_token: login.refresh,
2348
+ client_id: "opencode-cli"
2349
+ }))).pipe(map8(refreshed(now))),
2350
+ "github-copilot": (login) => answer("github-copilot", CopilotAnswer, get("https://api.github.com/copilot_internal/v2/token").pipe(setHeaders({ authorization: `Bearer ${login.refresh}`, accept: "application/json" }))).pipe(map8((answered) => ({ access: answered.token, expires: answered.expires_at * 1000 })))
2351
+ };
2352
+ var renewerFor = (provider) => isKeyOf(renewers, provider) ? renewers[provider] : undefined;
2353
+ var renewableLogin = (provider, credential) => getOrUndefined(filter2(decodeRenewable(credential), (login) => renewerFor(provider) !== undefined && (provider !== "github-copilot" || login.access !== login.refresh)));
2354
+ var renewsOpenCodeCredential = (provider, credential) => renewableLogin(provider, credential) !== undefined;
2355
+ var openCodeExpiry = (credentials, provider) => renewableLogin(provider, credentials?.[provider])?.expires;
2356
+ var replaceStored = (binary, source, store, stored, value) => gen(function* () {
2357
+ const fs = yield* FileSystem;
2358
+ const path = yield* Path;
2359
+ const directory = yield* fs.makeTempDirectoryScoped({
2360
+ directory: path.dirname(source),
2361
+ prefix: "kitman-opencode-renewal-"
2362
+ });
2363
+ const file = path.join(directory, "credentials.json");
2364
+ const id = `cred_kitman${(yield* currentTimeMillis).toString(36)}${Math.abs(yield* nextInt).toString(36)}`;
2365
+ yield* fs.writeFileString(file, encodeUnknownJsonStringSync([
2366
+ { id, integrationID: stored.integrationID, label: stored.label ?? "", value, active: true }
2367
+ ]), { mode: 384 });
2368
+ yield* runOpenCode(binary, store, ["auth", "import", file]);
2369
+ yield* runOpenCode(binary, store, ["api", "credential.activate", "--param", `credentialID=${id}`]);
2370
+ yield* runOpenCode(binary, store, ["api", "credential.remove", "--param", `credentialID=${stored.id}`]);
2371
+ }).pipe(scoped);
2372
+ var replaceInFile = (source, provider, credential) => gen(function* () {
2373
+ const fs = yield* FileSystem;
2374
+ const current = (yield* fileCredentials(source)) ?? {};
2375
+ const next = `${source}.${Math.abs(yield* nextInt)}.kitman-next`;
2376
+ yield* fs.writeFileString(next, encodeUnknownJsonStringSync(Object.assign({}, current, { [provider]: credential })), { mode: 384 });
2377
+ yield* fs.rename(next, source);
2378
+ });
2379
+ var renewalPermits = new Map;
2380
+ var oneRenewalAt = (source) => (effect) => {
2381
+ const permit = renewalPermits.get(source) ?? makeUnsafe3(1);
2382
+ renewalPermits.set(source, permit);
2383
+ return permit.withPermits(1)(effect);
2384
+ };
2385
+ var renewOpenCodeLogin = (binary, source, provider, seenExpiry) => gen(function* () {
2386
+ const login = yield* readOpenCodeLogin(binary, source);
2387
+ const credential = login?.credentials[provider];
2388
+ const renewable = renewableLogin(provider, credential);
2389
+ const renew = renewerFor(provider);
2390
+ if (login === undefined || renewable === undefined || renew === undefined || !isJsonObject(credential))
2391
+ return yield* new OpenCodeLoginNotRenewed({ provider, reason: "it holds no OAuth login the Runner renews" });
2392
+ if (seenExpiry !== undefined && renewable.expires > seenExpiry)
2393
+ return;
2394
+ const stored = login.stored?.[provider];
2395
+ const metadata = isJsonObject(stored?.value.metadata) ? stored.value.metadata : {};
2396
+ const renewed = yield* renew(renewable, yield* currentTimeMillis, metadata);
2397
+ const tokens = { access: renewed.access, refresh: renewed.refresh ?? renewable.refresh, expires: renewed.expires };
2398
+ const store = yield* openCodeStore(source);
2399
+ yield* (stored === undefined || store === undefined ? replaceInFile(source, provider, Object.assign({}, credential, tokens)) : replaceStored(binary, source, store, stored, Object.assign({}, stored.value, tokens))).pipe(mapError2((error) => new OpenCodeLoginNotRenewed({ provider, reason: error.message })));
2400
+ }).pipe(oneRenewalAt(source));
2401
+
2103
2402
  // packages/agent-runtime/src/skill-fits.ts
2104
2403
  import { matchesGlob } from "path";
2105
2404
  var skipped = new Set([".git", "node_modules"]);
@@ -2186,13 +2485,17 @@ var stepSandboxPolicy = (input) => ({
2186
2485
  readDeniedPaths: [
2187
2486
  ...input.policy.readDeniedPaths,
2188
2487
  ...HarnessAdapters.filter((other) => other.id !== input.adapter.id).flatMap((other) => other.auth),
2189
- ...input.adapter.agentDir === undefined ? [] : [`~/${input.adapter.agentDir.operatorDir}/${input.adapter.agentDir.login.file}`],
2488
+ ...input.adapter.agentDir === undefined ? [] : [
2489
+ input.adapter.agentDir.login.file,
2490
+ ...input.adapter.agentDir.providers === undefined ? [] : [input.adapter.agentDir.providers]
2491
+ ].map((file) => `~/${input.adapter.agentDir?.operatorDir}/${file}`),
2190
2492
  ...input.adapter.renewedLogin === undefined ? [] : [`~/${input.adapter.renewedLogin.operatorDir}/${input.adapter.renewedLogin.file}`],
2191
- ...input.adapter.sandboxHome === undefined ? [] : [`~/${input.adapter.sandboxHome.login.source}`]
2493
+ ...input.adapter.sandboxHome === undefined ? [] : [`~/${input.adapter.sandboxHome.login.source.replace(/\/[^/]+$/, "")}`],
2494
+ ...input.launch?.globalConfig === undefined ? [] : [input.launch.globalConfig.source]
2192
2495
  ],
2193
2496
  readAllowedPaths: [
2194
2497
  ...input.policy.readAllowedPaths,
2195
- ...input.adapter.loginFiles ?? [],
2498
+ ...input.launch?.globalConfig === undefined ? input.adapter.loginFiles ?? [] : [],
2196
2499
  input.workspace,
2197
2500
  input.configDir
2198
2501
  ],
@@ -2297,8 +2600,90 @@ var credentialsIn = (source) => gen(function* () {
2297
2600
  const credentials = getOrUndefined(decodeJsonStringOption(text));
2298
2601
  return isJsonObject(credentials) ? credentials : undefined;
2299
2602
  });
2603
+ var loginCredentials = (login) => login.format === "opencode" ? map8(readOpenCodeLogin(login.binary, login.source), (read) => read?.credentials) : credentialsIn(login.source);
2604
+ var neverExpires = 253402300799000;
2300
2605
  var isOAuth = (credential) => isJsonObject(credential) && credential.type === "oauth";
2301
2606
  var claudeOAuth = "claudeAiOauth";
2607
+ var piVariable = /\$(?:([$!])|\{([A-Za-z_]\w*)\}|([A-Za-z_]\w*))?/g;
2608
+ var isPiCommand = (value) => value.startsWith("!");
2609
+ var piResolved = (value, env = {}) => {
2610
+ if (isPiCommand(value))
2611
+ return;
2612
+ let resolved = true;
2613
+ const text = value.replace(piVariable, (dollar, escaped, braced, bare) => {
2614
+ const name = braced ?? bare;
2615
+ if (name === undefined)
2616
+ return escaped ?? dollar;
2617
+ const found = env[name];
2618
+ if (isString(found) && found !== "")
2619
+ return found;
2620
+ resolved = false;
2621
+ return dollar;
2622
+ });
2623
+ return resolved ? text : undefined;
2624
+ };
2625
+ var piNames = (value) => isPiCommand(value) ? [] : [...value.matchAll(piVariable)].flatMap((match) => match[2] ?? match[3] ?? []);
2626
+ var piConfiguringEnv = new Set([
2627
+ "AWS_BEDROCK_FORCE_CACHE",
2628
+ "AWS_DEFAULT_REGION",
2629
+ "AWS_REGION",
2630
+ "AZURE_OPENAI_API_VERSION",
2631
+ "AZURE_OPENAI_BASE_URL",
2632
+ "AZURE_OPENAI_DEPLOYMENT_NAME_MAP",
2633
+ "AZURE_OPENAI_RESOURCE_NAME",
2634
+ "CLOUDFLARE_ACCOUNT_ID",
2635
+ "CLOUDFLARE_GATEWAY_ID",
2636
+ "GCLOUD_PROJECT",
2637
+ "GOOGLE_CLOUD_LOCATION",
2638
+ "GOOGLE_CLOUD_PROJECT",
2639
+ "PI_CACHE_RETENTION"
2640
+ ]);
2641
+ var piKey = (key, credential) => piResolved(key, isJsonObject(credential.env) ? credential.env : {});
2642
+ var withConfiguringEnv = (credential, field) => {
2643
+ if (!isJsonObject(credential.env))
2644
+ return credential;
2645
+ const named = isString(credential[field]) ? piNames(credential[field]) : [];
2646
+ return Object.assign({}, credential, {
2647
+ env: Object.fromEntries(Object.entries(credential.env).filter(([name]) => piConfiguringEnv.has(name) && !named.includes(name)))
2648
+ });
2649
+ };
2650
+ var referenceNamePattern = /^[A-Za-z0-9_.-]+$/;
2651
+ var perProviderPlaceholders = (tokenFields, keyValue = (key) => key, isCommand = () => false) => (credentials, login, reference) => {
2652
+ const kept = [];
2653
+ const tokens = {};
2654
+ const held = (name, token, hosts) => {
2655
+ if (hosts.length > 0)
2656
+ tokens[name] = { value: token, hosts };
2657
+ return secretPlaceholder(token, name);
2658
+ };
2659
+ const placed = Object.entries(credentials).flatMap(([provider, credential]) => {
2660
+ const hosts = login.hosts[provider];
2661
+ const refreshHosts = login.refreshTokenHosts?.[provider];
2662
+ const field = isJsonObject(credential) && isString(credential.type) ? tokenFields[credential.type] : undefined;
2663
+ if (hosts === undefined && refreshHosts === undefined || field === undefined || !isJsonObject(credential) || !isString(credential[field]) || !referenceNamePattern.test(provider))
2664
+ return [];
2665
+ const token = credential.type === "oauth" ? credential[field] : keyValue(credential[field], credential);
2666
+ if (credential.type !== "oauth" && isCommand(credential[field]))
2667
+ return [];
2668
+ if (token === undefined) {
2669
+ kept.push([provider, withConfiguringEnv(credential, field)]);
2670
+ return [];
2671
+ }
2672
+ const name = reference(`${login.referenceName}-${provider}`);
2673
+ const refresh = isOAuth(credential) ? credential.refresh : undefined;
2674
+ const tokenHosts = [...new Set([...hosts ?? [], ...refreshHosts ?? []])];
2675
+ const heldRefresh = !isOAuth(credential) ? undefined : {
2676
+ refresh: refreshHosts !== undefined && isString(refresh) && refresh !== "" ? held(reference(`${login.referenceName}-${provider}-refresh`), refresh, refreshHosts) : ""
2677
+ };
2678
+ return [
2679
+ [
2680
+ provider,
2681
+ Object.assign({}, withConfiguringEnv(credential, field), { [field]: token === "" ? "" : held(name, token, tokenHosts) }, heldRefresh)
2682
+ ]
2683
+ ];
2684
+ });
2685
+ return { held: Object.fromEntries([...kept, ...placed]), tokens };
2686
+ };
2302
2687
  var loginFormats = {
2303
2688
  pi: {
2304
2689
  expiry: (credentials, provider) => {
@@ -2309,6 +2694,7 @@ var loginFormats = {
2309
2694
  provider,
2310
2695
  isOAuth(credential) ? Object.assign({}, credential, { refresh: "" }) : credential
2311
2696
  ])),
2697
+ byReference: perProviderPlaceholders({ oauth: "access", api_key: "key" }, piKey, isPiCommand),
2312
2698
  exitMeansRenewed: true
2313
2699
  },
2314
2700
  claude: {
@@ -2320,22 +2706,58 @@ var loginFormats = {
2320
2706
  const credential = credentials[claudeOAuth];
2321
2707
  return isJsonObject(credential) ? { [claudeOAuth]: Object.assign({}, credential, { refreshToken: "" }) } : undefined;
2322
2708
  },
2709
+ byReference: (credentials, login, reference) => {
2710
+ const credential = credentials[claudeOAuth];
2711
+ const hosts = login.hosts[claudeOAuth];
2712
+ if (!isJsonObject(credential) || !isString(credential.accessToken) || hosts === undefined)
2713
+ return;
2714
+ const name = reference(login.referenceName);
2715
+ return {
2716
+ held: { [claudeOAuth]: Object.assign({}, credential, { accessToken: name, refreshToken: "" }) },
2717
+ tokens: { [name]: { value: credential.accessToken, hosts } }
2718
+ };
2719
+ },
2720
+ exitMeansRenewed: false
2721
+ },
2722
+ opencode: {
2723
+ expiry: openCodeExpiry,
2724
+ withoutRefresh: (credentials) => Object.fromEntries(Object.entries(credentials).map(([provider, credential]) => [
2725
+ provider,
2726
+ isOAuth(credential) ? Object.assign({}, credential, { refresh: "" }) : credential
2727
+ ])),
2728
+ byReference: (credentials, login, reference) => {
2729
+ const consoleHosts = [...new Set([...login.hosts.opencode ?? [], ...openCodeConsoleHosts(credentials)])];
2730
+ const withConsole = { ...login, hosts: { ...login.hosts, opencode: consoleHosts } };
2731
+ const placeholders = perProviderPlaceholders({ oauth: "access", api: "key" });
2732
+ const { held, tokens } = placeholders(credentials, withConsole, reference);
2733
+ return {
2734
+ held: Object.fromEntries(Object.entries(held).map(([provider, credential]) => [
2735
+ provider,
2736
+ !isOAuth(credential) ? credential : provider === "opencode" ? openCodeConsoleKey(credential) : renewsOpenCodeCredential(provider, credentials[provider]) ? Object.assign({}, credential, { expires: neverExpires }) : credential
2737
+ ])),
2738
+ tokens
2739
+ };
2740
+ },
2323
2741
  exitMeansRenewed: false
2324
2742
  }
2325
2743
  };
2326
- var refreshOperatorLogin = (binary, login, expired) => gen(function* () {
2744
+ var refreshOperatorLogin = (login, expired, http) => gen(function* () {
2327
2745
  const format = loginFormats[login.format];
2328
2746
  const now = yield* currentTimeMillis;
2329
2747
  const expiringBefore = now + toMillis(login.refresh.within);
2330
- const credentials = yield* credentialsIn(login.source);
2748
+ const credentials = yield* loginCredentials(login);
2331
2749
  const due = Object.keys(credentials ?? {}).flatMap((provider) => {
2332
2750
  const expires = format.expiry(credentials, provider);
2333
2751
  const waiting = (expired.get(provider)?.retryAt ?? 0) > now;
2334
2752
  return expires === undefined || expires >= expiringBefore || expires <= now && waiting ? [] : [{ provider, expires }];
2335
2753
  });
2336
2754
  for (const { provider, expires } of due) {
2337
- const ran = yield* runCommand2(binary, login.refresh.args.map((arg) => arg.replace("{provider}", provider)), { env: login.refresh.env, timeout: "30 seconds" }).pipe(as(true), orElseSucceed(() => false), uninterruptible);
2338
- if (ran && format.exitMeansRenewed || (format.expiry(yield* credentialsIn(login.source), provider) ?? expires) > expires) {
2755
+ const renewal = login.format === "opencode" ? renewOpenCodeLogin(login.binary, login.source, provider, expires).pipe(provide3(http === undefined ? layer4 : succeed5(HttpClient, http)), as(true), orElseSucceed(() => false)) : runCommand2(login.binary, login.refresh.args.map((arg) => arg.replace("{provider}", provider)), {
2756
+ env: login.refresh.env,
2757
+ timeout: "30 seconds"
2758
+ }).pipe(as(true), orElseSucceed(() => false));
2759
+ const ran = yield* renewal.pipe(uninterruptible);
2760
+ if (ran && format.exitMeansRenewed || (format.expiry(yield* loginCredentials(login), provider) ?? expires) > expires) {
2339
2761
  expired.delete(provider);
2340
2762
  continue;
2341
2763
  }
@@ -2346,37 +2768,35 @@ var refreshOperatorLogin = (binary, login, expired) => gen(function* () {
2346
2768
  }
2347
2769
  }
2348
2770
  });
2349
- var referencedClaudeLogin = (held, reference) => {
2350
- const credential = held[claudeOAuth];
2351
- return isJsonObject(credential) && isString(credential.accessToken) ? {
2352
- token: credential.accessToken,
2353
- held: { ...held, [claudeOAuth]: Object.assign({}, credential, { accessToken: reference }) }
2354
- } : undefined;
2355
- };
2356
- var stepLogin = (login, reference) => map8(credentialsIn(login.source), (credentials) => {
2357
- const held = credentials === undefined ? undefined : loginFormats[login.format].withoutRefresh(credentials);
2358
- if (held === undefined)
2771
+ var stepLogin = (login, reference) => map8(loginCredentials(login), (credentials) => {
2772
+ if (credentials === undefined)
2359
2773
  return;
2360
- const referenced = reference === undefined || login.format !== "claude" ? undefined : referencedClaudeLogin(held, reference);
2361
- return referenced === undefined ? { content: encodeUnknownJsonStringSync(held) } : { content: encodeUnknownJsonStringSync(referenced.held), token: referenced.token };
2774
+ const format = loginFormats[login.format];
2775
+ if (reference === undefined) {
2776
+ const held = format.withoutRefresh(credentials);
2777
+ return held === undefined ? undefined : { content: encodeUnknownJsonStringSync(held), tokens: {} };
2778
+ }
2779
+ const held = format.byReference(credentials, login, reference);
2780
+ return held === undefined ? undefined : { content: encodeUnknownJsonStringSync(held.held), tokens: held.tokens };
2362
2781
  });
2363
- var renewableStepLogin = (binary, login, referenced) => gen(function* () {
2782
+ var renewableStepLogin = (login, referenced, http) => gen(function* () {
2364
2783
  const fs = yield* FileSystem;
2365
2784
  const path = yield* Path;
2366
2785
  const directory = yield* fs.makeTempDirectoryScoped({ prefix: "kitman-login-" });
2367
2786
  const file = path.join(directory, path.basename(login.source));
2368
2787
  const expired = new Map;
2369
2788
  let published;
2370
- let heldToken;
2789
+ let heldTokens;
2371
2790
  const writing = makeUnsafe3(1);
2372
2791
  const refreshing = makeUnsafe3(1);
2373
2792
  const copy = gen(function* () {
2374
2793
  const copied = yield* stepLogin(login, referenced?.reference);
2375
2794
  if (copied === undefined)
2376
2795
  return;
2377
- if (copied.token !== undefined && copied.token !== heldToken && referenced !== undefined) {
2378
- yield* referenced.hold(copied.token);
2379
- heldToken = copied.token;
2796
+ const tokens = encodeUnknownJsonStringSync(copied.tokens);
2797
+ if (referenced !== undefined && tokens !== heldTokens) {
2798
+ yield* referenced.hold(copied.tokens);
2799
+ heldTokens = tokens;
2380
2800
  }
2381
2801
  if (copied.content === published)
2382
2802
  return;
@@ -2384,17 +2804,154 @@ var renewableStepLogin = (binary, login, referenced) => gen(function* () {
2384
2804
  yield* fs.rename(`${file}.next`, file);
2385
2805
  published = copied.content;
2386
2806
  }).pipe((saving) => writing.withPermit(saving));
2387
- const publish = (refresh) => refresh ? refreshing.withPermit(refreshOperatorLogin(binary, login, expired)).pipe(andThen2(copy)) : copy;
2807
+ const publish = (refresh) => refresh ? refreshing.withPermit(refreshOperatorLogin(login, expired, http)).pipe(andThen2(copy)) : copy;
2388
2808
  return { directory, file, publish, isPublished: () => published !== undefined };
2389
2809
  });
2810
+ var loopbackHosts = new Set(["localhost", "127.0.0.1", "[::1]"]);
2811
+ var keyDestination = (baseUrl, fallback) => {
2812
+ if (!isString(baseUrl))
2813
+ return { hosts: fallback };
2814
+ const url = getOrUndefined(liftThrowable(() => new URL(baseUrl))());
2815
+ if (url?.protocol === "https:")
2816
+ return { hosts: [url.host] };
2817
+ return { hosts: undefined, local: url?.protocol === "http:" && loopbackHosts.has(url.hostname) };
2818
+ };
2819
+ var readProviders = (source) => gen(function* () {
2820
+ const fs = yield* FileSystem;
2821
+ const text = yield* fs.readFileString(source).pipe(orElseSucceed(() => {
2822
+ return;
2823
+ }));
2824
+ const config = text === undefined ? undefined : getOrUndefined(decodeJsonStringOption(text));
2825
+ return { text, config: isJsonObject(config) ? config : undefined };
2826
+ });
2827
+ var objectEntries = (value) => isJsonObject(value) ? Object.entries(value) : [];
2828
+ var inheritedDestination = (destinations) => {
2829
+ const hosts = [...new Set(destinations.flatMap(({ hosts }) => hosts ?? []))];
2830
+ return hosts.length > 0 ? { hosts } : { hosts: undefined, local: destinations.some(({ local }) => local === true) };
2831
+ };
2832
+ var providerDestinations = (config, hosts) => objectEntries(config?.providers).flatMap(([name, provider]) => {
2833
+ if (!isJsonObject(provider))
2834
+ return [];
2835
+ const destination = keyDestination(provider.baseUrl, hosts[name]);
2836
+ const models = (Array.isArray(provider.models) ? provider.models : []).map((model) => isJsonObject(model) && isString(model.baseUrl) ? keyDestination(model.baseUrl, destination.hosts) : destination);
2837
+ return [{ name, provider, destination, models, inherited: inheritedDestination([destination, ...models]) }];
2838
+ });
2839
+ var loginHostsWithProviders = (operatorFiles) => gen(function* () {
2840
+ const { login, providers } = operatorFiles;
2841
+ if (providers === undefined)
2842
+ return login;
2843
+ const { config } = yield* readProviders(providers.source);
2844
+ const hosts = {
2845
+ ...login.hosts,
2846
+ ...Object.fromEntries(providerDestinations(config, providers.hosts).flatMap(({ name, inherited }) => inherited.hosts !== undefined ? [[name, inherited.hosts]] : inherited.local === true ? [[name, []]] : []))
2847
+ };
2848
+ return { ...login, hosts };
2849
+ });
2850
+ var carriedLoginHosts = (launch) => launch.renewedLogin?.format === "opencode" ? map8(loginCredentials(launch.renewedLogin), openCodeConsoleHosts) : succeed6([]);
2851
+ var carriedProviderHosts = (launch) => gen(function* () {
2852
+ const providers = launch.operatorFiles?.providers;
2853
+ if (providers === undefined)
2854
+ return [];
2855
+ const { config } = yield* readProviders(providers.source);
2856
+ return [
2857
+ ...new Set(providerDestinations(config, providers.hosts).flatMap(({ destination, models }) => [destination, ...models].flatMap(({ hosts }) => hosts ?? [])))
2858
+ ];
2859
+ });
2860
+ var carriedProviders = (providers, referenced) => gen(function* () {
2861
+ const { text, config } = yield* readProviders(providers.source);
2862
+ if (text === undefined)
2863
+ return [];
2864
+ if (referenced === undefined)
2865
+ return [{ path: providers.path, content: text }];
2866
+ if (config === undefined || !isJsonObject(config.providers))
2867
+ return [{ path: providers.path, content: "{}" }];
2868
+ const tokens = {};
2869
+ const placeholder = (field, value, destination) => {
2870
+ if (!isString(value))
2871
+ return value;
2872
+ if (isPiCommand(value))
2873
+ return;
2874
+ const literal = piResolved(value);
2875
+ if (literal === undefined)
2876
+ return value;
2877
+ if (destination.hosts === undefined && destination.local !== true)
2878
+ return;
2879
+ const reference = referenced.reference(`${providers.referenceName}-${Object.keys(tokens).length}-${field}`.replace(/[^A-Za-z0-9_.-]/g, "-"));
2880
+ if (destination.hosts !== undefined)
2881
+ tokens[reference] = { value: literal, hosts: destination.hosts };
2882
+ return secretPlaceholder(literal, reference);
2883
+ };
2884
+ const heldEntry = (entry, field, destination) => {
2885
+ const { apiKey, headers, ...rest } = entry;
2886
+ const heldHeaders = isJsonObject(headers) ? Object.fromEntries(Object.entries(headers).flatMap(([header, value]) => {
2887
+ const kept = placeholder(`${field}-header-${header}`, value, destination);
2888
+ return kept === undefined ? [] : [[header, kept]];
2889
+ })) : undefined;
2890
+ const key = placeholder(`${field}-key`, apiKey, destination);
2891
+ return {
2892
+ ...rest,
2893
+ ...key === undefined ? undefined : { apiKey: key },
2894
+ ...heldHeaders === undefined ? undefined : { headers: heldHeaders }
2895
+ };
2896
+ };
2897
+ const held = Object.fromEntries(providerDestinations(config, providers.hosts).map(({ name, provider, destination, models, inherited }) => {
2898
+ const entry = heldEntry(provider, name, inherited);
2899
+ const heldModels = Array.isArray(provider.models) ? provider.models.map((model, index) => isJsonObject(model) ? heldEntry(model, `${name}-model-${index}`, models[index] ?? destination) : model) : undefined;
2900
+ const overrides = isJsonObject(provider.modelOverrides) ? Object.fromEntries(Object.entries(provider.modelOverrides).map(([id, override]) => [
2901
+ id,
2902
+ isJsonObject(override) ? heldEntry(override, `${name}-override-${id}`, destination) : override
2903
+ ])) : undefined;
2904
+ return [
2905
+ name,
2906
+ {
2907
+ ...entry,
2908
+ ...heldModels === undefined ? undefined : { models: heldModels },
2909
+ ...overrides === undefined ? undefined : { modelOverrides: overrides }
2910
+ }
2911
+ ];
2912
+ }));
2913
+ yield* referenced.hold(tokens);
2914
+ return [
2915
+ { path: providers.path, content: encodeUnknownJsonStringSync(Object.assign({}, config, { providers: held })) }
2916
+ ];
2917
+ });
2918
+ var withoutMcpServers = (config) => {
2919
+ const { mcpServers: _servers, projects, ...rest } = config;
2920
+ if (!isJsonObject(projects))
2921
+ return projects === undefined ? rest : { ...rest, projects };
2922
+ return {
2923
+ ...rest,
2924
+ projects: Object.fromEntries(Object.entries(projects).map(([directory, project]) => {
2925
+ if (!isJsonObject(project))
2926
+ return [directory, project];
2927
+ const { mcpServers: _projectServers, ...kept } = project;
2928
+ return [directory, kept];
2929
+ }))
2930
+ };
2931
+ };
2932
+ var carriedGlobalConfig = (config, referenced) => gen(function* () {
2933
+ const operator = yield* credentialsIn(config.source);
2934
+ if (operator === undefined)
2935
+ return [];
2936
+ const { [config.keyField]: key, ...rest } = withoutMcpServers(operator);
2937
+ if (!isString(key) || referenced === undefined)
2938
+ return [{ path: config.path, content: encodeUnknownJsonStringSync(rest) }];
2939
+ const reference = referenced.reference(config.referenceName);
2940
+ yield* referenced.hold({ [reference]: { value: key, hosts: config.hosts } });
2941
+ return [
2942
+ {
2943
+ path: config.path,
2944
+ content: encodeUnknownJsonStringSync({ ...rest, [config.keyField]: secretPlaceholder(key, reference) })
2945
+ }
2946
+ ];
2947
+ });
2390
2948
  var writeHarnessFiles = (input) => gen(function* () {
2391
2949
  const fs = yield* FileSystem;
2392
2950
  const path = yield* Path;
2393
2951
  const unwritable = harnessConfigUnwritable();
2394
2952
  const carried = input.carryOperatorFiles ? input.launch.operatorFiles : undefined;
2395
- const carriesLogin = input.carryOperatorFiles;
2396
- const renewed = carriesLogin ? carried?.login ?? input.launch.renewedLogin : undefined;
2397
- const login = renewed === undefined ? undefined : yield* renewableStepLogin(input.launch.binary, renewed, input.loginReference).pipe(orDie);
2953
+ const renewed = !input.carryOperatorFiles ? undefined : carried === undefined ? input.launch.renewedLogin : yield* loginHostsWithProviders(carried);
2954
+ const login = renewed === undefined ? undefined : yield* renewableStepLogin(renewed, input.loginReference, input.loginHttp).pipe(orDie);
2398
2955
  if (login !== undefined)
2399
2956
  yield* login.publish(true).pipe(mapError2(() => unwritable));
2400
2957
  const settings = carried === undefined ? [] : [
@@ -2403,11 +2960,14 @@ var writeHarnessFiles = (input) => gen(function* () {
2403
2960
  content: encodeUnknownJsonStringSync(yield* carriedSettings(carried.settings.source, carried.settings.keys))
2404
2961
  }
2405
2962
  ];
2406
- const files = [...input.launch.files, ...settings];
2407
- const loginCopy = carriesLogin ? input.launch.loginCopy : undefined;
2408
- const copies = [...carried?.copies ?? [], ...loginCopy === undefined ? [] : [loginCopy]];
2963
+ const providers = carried?.providers === undefined ? [] : yield* carriedProviders(carried.providers, input.loginReference).pipe(mapError2(() => unwritable));
2964
+ const globalConfig = input.carryOperatorFiles ? input.launch.globalConfig : undefined;
2965
+ const globalConfigFiles = globalConfig === undefined ? [] : yield* carriedGlobalConfig(globalConfig, input.loginReference).pipe(mapError2(() => unwritable));
2966
+ const files = [...input.launch.files, ...settings, ...providers, ...globalConfigFiles];
2967
+ const copies = carried?.copies ?? [];
2409
2968
  const links = renewed === undefined ? [] : [renewed];
2410
- if ([...files, ...copies, ...links].some((file) => !isPathWithin2(path, input.configDir, file.path)))
2969
+ const globalConfigLinks = globalConfig?.links ?? [];
2970
+ if ([...files, ...copies, ...links, ...globalConfigLinks].some((file) => !isPathWithin2(path, input.configDir, file.path)))
2411
2971
  return yield* unwritable;
2412
2972
  for (const file of files) {
2413
2973
  const { path } = file;
@@ -2417,14 +2977,17 @@ var writeHarnessFiles = (input) => gen(function* () {
2417
2977
  if (yield* fs.exists(copy.source).pipe(orElseSucceed(() => false)))
2418
2978
  yield* fs.makeDirectory(path.dirname(copy.path), { recursive: true }).pipe(andThen2(fs.copyFile(copy.source, copy.path)), mapError2(() => unwritable));
2419
2979
  }
2980
+ for (const link of globalConfigLinks)
2981
+ yield* fs.makeDirectory(link.target, { recursive: true }).pipe(andThen2(fs.makeDirectory(path.dirname(link.path), { recursive: true })), andThen2(fs.symlink(link.target, link.path)), mapError2(() => unwritable));
2420
2982
  if (input.carryOperatorFiles && input.launch.homeDirectory !== undefined)
2421
2983
  yield* fs.makeDirectory(input.launch.homeDirectory, { recursive: true }).pipe(mapError2(() => unwritable));
2422
2984
  if (renewed === undefined || login === undefined || !login.isPublished())
2423
2985
  return;
2424
- yield* fs.symlink(renewed.linksDirectory === true ? login.directory : login.file, renewed.path).pipe(mapError2(() => unwritable));
2986
+ yield* fs.makeDirectory(path.dirname(renewed.path), { recursive: true }).pipe(andThen2(fs.symlink(renewed.linksDirectory === true ? login.directory : login.file, renewed.path)), mapError2(() => unwritable));
2425
2987
  const every = (millis, refresh) => forkScoped(forever(sleep(millis).pipe(andThen2(login.publish(refresh)), catch_((error) => logWarning("harness_login_not_renewed", error)))));
2426
2988
  yield* every(loginRenewalMillis, true);
2427
- yield* every(loginCopyMillis, false);
2989
+ if (renewed.format !== "opencode")
2990
+ yield* every(loginCopyMillis, false);
2428
2991
  const watching = makeUnsafe5();
2429
2992
  yield* forkScoped(fs.watch(path.dirname(renewed.source), { onReady: () => doneUnsafe(watching, void_3) }).pipe(filter((event) => path.basename(event.path) === path.basename(renewed.source)), runForEach(() => login.publish(false)), catch_((error) => logWarning("harness_login_not_watched", error)), ensuring2(succeed4(watching, undefined))));
2430
2993
  yield* _await(watching);
@@ -3067,8 +3630,7 @@ var transcriptSource = (adapter, place) => gen(function* () {
3067
3630
  class SecretRegistryUnwritable extends TaggedError("SecretRegistryUnwritable") {
3068
3631
  }
3069
3632
  var runnerSecretOwner = "runner";
3070
- var claudeLoginReference = secretReference(runnerSecretOwner, "claude-code-login");
3071
- var holdsLoginByReference = (launch) => launch.renewedLogin?.format === "claude";
3633
+ var carriesLogin = (launch) => [launch.renewedLogin, launch.operatorFiles?.login, launch.operatorFiles?.providers, launch.globalConfig].some((file) => file !== undefined);
3072
3634
  var stepSecretDirectory = (home, attemptId) => `${home}/.cache/kitman/step-secrets/${attemptId}`;
3073
3635
  var requireSecretHosts = (secrets, destinations) => {
3074
3636
  const unbound = Object.keys(secrets ?? {}).filter((name) => (destinations?.[name]?.hosts.length ?? 0) === 0);
@@ -3317,7 +3879,12 @@ var runAttempt = (request, options) => gen(function* () {
3317
3879
  workspace,
3318
3880
  configDir,
3319
3881
  environment: { ...agent.secrets, ...launch.env, ...mcp.env },
3320
- hosts: kitMcpHosts(kitSetup)
3882
+ hosts: [
3883
+ ...kitMcpHosts(kitSetup),
3884
+ ...yield* carriedProviderHosts(launch),
3885
+ ...yield* carriedLoginHosts(launch)
3886
+ ],
3887
+ launch
3321
3888
  });
3322
3889
  const harnessArgv = [launch.binary, ...launch.args];
3323
3890
  const sandboxed = gen(function* () {
@@ -3338,7 +3905,7 @@ var runAttempt = (request, options) => gen(function* () {
3338
3905
  { value, hosts: providerKeyHosts.get(name) ?? [] }
3339
3906
  ]))
3340
3907
  };
3341
- const holdsLogin = holdsLoginByReference(launch);
3908
+ const holdsLogin = carriesLogin(launch);
3342
3909
  const registry = Object.keys(initial).length === 0 && !holdsLogin ? undefined : yield* holdSecrets(initial);
3343
3910
  const withSessions = sessions === undefined ? policy : {
3344
3911
  ...policy,
@@ -3422,15 +3989,17 @@ var runAttempt = (request, options) => gen(function* () {
3422
3989
  launch,
3423
3990
  configDir,
3424
3991
  carryOperatorFiles: request.sandbox.backend !== "docker",
3425
- ...registry === undefined || !holdsLoginByReference(launch) ? undefined : {
3992
+ ...registry === undefined ? undefined : {
3426
3993
  loginReference: {
3427
- reference: claudeLoginReference,
3428
- hold: (token) => registry.hold(claudeLoginReference, { value: token, hosts: adapter.providerHosts })
3994
+ reference: (name) => secretReference(runnerSecretOwner, name),
3995
+ hold: (tokens) => forEach(Object.entries(tokens), ([reference, entry]) => registry.hold(reference, entry), {
3996
+ discard: true
3997
+ })
3429
3998
  }
3430
3999
  }
3431
4000
  }), registry === undefined ? void_3 : watchSecretRegistry(registry, request, options.secretRevocationMs))));
3432
4001
  if (adapter.id === "claude" && request.sandbox.backend !== "docker")
3433
- yield* trustClaudeWorkspace(options.home, workspace).pipe(catch_((error) => logWarning("claude_trust_failed", error)));
4002
+ yield* trustClaudeWorkspace(launch.globalConfig === undefined ? options.home : (yield* Path).dirname(launch.globalConfig.path), workspace).pipe(catch_((error) => logWarning("claude_trust_failed", error)));
3434
4003
  if (isFailure(command))
3435
4004
  return failed(command.failure.reason, command.failure.summary);
3436
4005
  const call = requester(options.core, yield* scope2);
@@ -3802,7 +4371,7 @@ var catchUpReport = (worktree, input) => gen(function* () {
3802
4371
  const settled = input.automerged === undefined ? [] : (yield* names(input.automerged, input.merged)).filter((path) => !input.conflicted.includes(path));
3803
4372
  const rewritten = [...new Set([...settled, ...yield* names(input.merged, input.head)])].sort();
3804
4373
  const paths = [...new Set([...yield* names(before, input.from), ...yield* names(after, input.head)])].sort();
3805
- const changed = yield* filter2(paths.slice(0, catchUpPathLimit), (path) => map8(all3([ownDiff(before, input.from, path), ownDiff(after, input.head, path)]), ([was, is]) => was !== is), { concurrency: 8 });
4374
+ const changed = yield* filter3(paths.slice(0, catchUpPathLimit), (path) => map8(all3([ownDiff(before, input.from, path), ownDiff(after, input.head, path)]), ([was, is]) => was !== is), { concurrency: 8 });
3806
4375
  const lists = [input.conflicted, rewritten, paths];
3807
4376
  return {
3808
4377
  from: input.from,
@@ -5419,7 +5988,7 @@ var agentStepSandboxPolicy = (work, apiUrl, runner) => ({
5419
5988
  network: agentNetworkAllowlist({
5420
5989
  apiUrl,
5421
5990
  storageOrigin: work.storageOrigin,
5422
- providerHosts: harnessAdapter(work.brief.harness)?.providerHosts ?? [],
5991
+ providerHosts: harnessNetworkHosts(harnessAdapter(work.brief.harness)),
5423
5992
  ...work.repoBinding === undefined ? undefined : { repoBindingHost: work.repoBinding.host },
5424
5993
  ...work.network === undefined ? undefined : { agent: work.network },
5425
5994
  ...work.hosts === undefined ? undefined : { allowed: work.hosts }
@@ -6465,7 +7034,7 @@ var regularFiles = (dir) => gen(function* () {
6465
7034
  const fs = yield* FileSystem;
6466
7035
  const path = yield* Path;
6467
7036
  const real = yield* fs.realPath(dir);
6468
- return yield* filter2((yield* fs.readDirectory(dir)).sort(), (name) => gen(function* () {
7037
+ return yield* filter3((yield* fs.readDirectory(dir)).sort(), (name) => gen(function* () {
6469
7038
  const entry = path.join(real, name);
6470
7039
  return (yield* fs.realPath(entry)) === entry && (yield* fs.stat(entry)).type === "File";
6471
7040
  }).pipe(orElseSucceed(() => false)));
@@ -6938,6 +7507,8 @@ export {
6938
7507
  applyAgentEnvironmentMaterialization2 as applyAgentEnvironmentMaterialization,
6939
7508
  applyAgentEnvironmentPlans,
6940
7509
  assertHerdrRunnerCapability2 as assertHerdrRunnerCapability,
7510
+ carriedLoginHosts,
7511
+ carriedProviderHosts,
6941
7512
  checkDockerAlias,
6942
7513
  checkDockerClientConfig,
6943
7514
  checkDockerDaemonArgs,
@@ -6971,6 +7542,7 @@ export {
6971
7542
  halfCloseListener,
6972
7543
  harnessAdapter,
6973
7544
  harnessConfigUnwritable,
7545
+ harnessNetworkHosts,
6974
7546
  harnessesInSrt,
6975
7547
  harnessesUnsupportedInSrt2 as harnessesUnsupportedInSrt,
6976
7548
  herdrAttemptLeased,