@orkestrel/scaffold 0.0.63 → 0.0.65

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 (57) hide show
  1. package/README.md +29 -104
  2. package/dist/bin/main.js +95 -27
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +2 -2
  5. package/dist/host/CLAUDE.md +6 -0
  6. package/dist/host/agents/orchestration.md +23 -15
  7. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +184 -177
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +314 -91
  9. package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +241 -0
  10. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +83 -36
  11. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +297 -98
  12. package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +25 -14
  13. package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +216 -128
  14. package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +187 -0
  15. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +109 -20
  16. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
  17. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  18. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
  19. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
  20. package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
  21. package/dist/host/claude/agents/orkestrel.md +56 -56
  22. package/dist/host/claude/agents/reviewer.md +13 -0
  23. package/dist/host/claude/rules/architecture.md +51 -45
  24. package/dist/host/claude/rules/documentation.md +18 -1
  25. package/dist/host/claude/rules/portability.md +2 -0
  26. package/dist/host/claude/rules/quality.md +1 -1
  27. package/dist/host/claude/rules/tests.md +12 -11
  28. package/dist/host/claude/rules/typescript.md +5 -0
  29. package/dist/host/claude/rules/workspace.md +25 -20
  30. package/dist/host/claude/rules/writing.md +4 -0
  31. package/dist/host/claude/settings.json +1 -1
  32. package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -9
  33. package/dist/host/codex/agents/orkestrel.toml +3 -3
  34. package/dist/host/codex/agents/reviewer.toml +4 -2
  35. package/dist/host/configs/helpers.ts +311 -2
  36. package/dist/host/configs/policy.ts +1100 -51
  37. package/dist/host/dotfiles/oxlintrc.json +72 -1
  38. package/dist/host/guides/guide.md +749 -222
  39. package/dist/host/guides/scaffold.md +529 -394
  40. package/dist/host/manifest.json +53 -40
  41. package/dist/host/scripts/ollama.sh +322 -13
  42. package/dist/host/tests/config.test.ts +1200 -16
  43. package/dist/host/tests/policy.test.ts +157 -173
  44. package/dist/host/tests/setupPolicy.ts +522 -1007
  45. package/dist/src/core/index.cjs +402 -287
  46. package/dist/src/core/index.cjs.map +1 -1
  47. package/dist/src/core/index.d.cts +160 -128
  48. package/dist/src/core/index.d.ts +160 -128
  49. package/dist/src/core/index.js +400 -286
  50. package/dist/src/core/index.js.map +1 -1
  51. package/dist/src/server/index.cjs +28 -21
  52. package/dist/src/server/index.cjs.map +1 -1
  53. package/dist/src/server/index.d.cts +38 -33
  54. package/dist/src/server/index.d.ts +38 -33
  55. package/dist/src/server/index.js +28 -21
  56. package/dist/src/server/index.js.map +1 -1
  57. package/package.json +18 -19
@@ -4,13 +4,13 @@
4
4
  "storage": "AGENTS.md",
5
5
  "destination": "AGENTS.md",
6
6
  "executable": false,
7
- "digest": "43ce847aa96a525919dbe948a2311214c312d58069054800577e5aff89024f08"
7
+ "digest": "365f62b880fac923347c8e78482bc8130559a15e5434e4aeb5cbd46b853ea6f6"
8
8
  },
9
9
  {
10
10
  "storage": "CLAUDE.md",
11
11
  "destination": "CLAUDE.md",
12
12
  "executable": false,
13
- "digest": "3428bb67c466f2880aab56f4a95129e482b6abf0b99e93e738ad790f578413ee"
13
+ "digest": "14c69316111917d29c42caed161d7983ff9eca488a7182d36fa0b04166e4f971"
14
14
  },
15
15
  {
16
16
  "storage": "LICENSE",
@@ -22,13 +22,13 @@
22
22
  "storage": "agents/orchestration.md",
23
23
  "destination": ".agents/orchestration.md",
24
24
  "executable": false,
25
- "digest": "5a03f0752db8449cfaf82c427917b1e9d8b3595313c46659afcfa927ac11cf0f"
25
+ "digest": "fd0b8846e67041e251e30009311b444cea8199f036c822ba9fc1a313ecfdfd69"
26
26
  },
27
27
  {
28
28
  "storage": "agents/skills/enterprise-bootstrap/SKILL.md",
29
29
  "destination": ".agents/skills/enterprise-bootstrap/SKILL.md",
30
30
  "executable": false,
31
- "digest": "6ed9e288539cbe7a7fbd25b13cf759ecedd72101189e0317fbabf82d1aa1aef4"
31
+ "digest": "f58af98acfa62de842476c17e71c1a98aad250cb4336a160019320a5dd6dacad"
32
32
  },
33
33
  {
34
34
  "storage": "agents/skills/enterprise-bootstrap/agents/openai.yaml",
@@ -40,37 +40,49 @@
40
40
  "storage": "agents/skills/enterprise-bootstrap/references/bootstrap-reference.md",
41
41
  "destination": ".agents/skills/enterprise-bootstrap/references/bootstrap-reference.md",
42
42
  "executable": false,
43
- "digest": "2221c8fddbf0474c9fbf39b5411275e27127940338b20e7626e5fac18f62c5f0"
43
+ "digest": "07ab83158aeeaabd965c285de6a30cbe28241e808bcad848fd6c7197fa6b9d12"
44
+ },
45
+ {
46
+ "storage": "agents/skills/enterprise-bootstrap/references/color-modes.md",
47
+ "destination": ".agents/skills/enterprise-bootstrap/references/color-modes.md",
48
+ "executable": false,
49
+ "digest": "c089bd32922e1d6ec74b55afc7cac184f6f8c58a75653647c26ddc814682bcdb"
44
50
  },
45
51
  {
46
52
  "storage": "agents/skills/enterprise-bootstrap/references/components.md",
47
53
  "destination": ".agents/skills/enterprise-bootstrap/references/components.md",
48
54
  "executable": false,
49
- "digest": "252de08e786aa41834f13df4d115e33b3c56d897ccea00b95ed4912da62fbd89"
55
+ "digest": "f4c9b5f2f8e7482aac2aebb0c3a837095c3ff9e8a309db1ee256673a7fdf2553"
50
56
  },
51
57
  {
52
58
  "storage": "agents/skills/enterprise-bootstrap/references/frontend-design.md",
53
59
  "destination": ".agents/skills/enterprise-bootstrap/references/frontend-design.md",
54
60
  "executable": false,
55
- "digest": "96bbc411f56b748f71d67238b8b10e8a04fe303853930c24035de0dc3428e307"
61
+ "digest": "90e5242046776118b18f0489e5cbc82ac498935dc8c29ec2644b53327d89955c"
56
62
  },
57
63
  {
58
64
  "storage": "agents/skills/enterprise-bootstrap/references/inputs.md",
59
65
  "destination": ".agents/skills/enterprise-bootstrap/references/inputs.md",
60
66
  "executable": false,
61
- "digest": "17fb8a977d34a0ef25f5155ac6459e805cc4f691d819995eb85838dca6c8fa46"
67
+ "digest": "7e536b0651a3347019b140d1fac29e00dba38234ae37a93e3c34281086da0cdf"
62
68
  },
63
69
  {
64
70
  "storage": "agents/skills/enterprise-bootstrap/references/inspection.md",
65
71
  "destination": ".agents/skills/enterprise-bootstrap/references/inspection.md",
66
72
  "executable": false,
67
- "digest": "48b202e331c17a26f8262b30095898e6d147906847a74d3779907c0aad4de43b"
73
+ "digest": "152548af42ef9322cfb398b9a6d64352f39c912c31ee324016c13959e4cfa34b"
74
+ },
75
+ {
76
+ "storage": "agents/skills/enterprise-bootstrap/references/responsive-layout.md",
77
+ "destination": ".agents/skills/enterprise-bootstrap/references/responsive-layout.md",
78
+ "executable": false,
79
+ "digest": "a933fdf530d4001cc9a75d24b103d111efc428d146d2c8cf9ea934891ca21019"
68
80
  },
69
81
  {
70
82
  "storage": "agents/skills/enterprise-bootstrap/references/utilities.md",
71
83
  "destination": ".agents/skills/enterprise-bootstrap/references/utilities.md",
72
84
  "executable": false,
73
- "digest": "d3a60ead829b6059e656795d388d74320ccfd6667d805a106fbed6cf778ba7b4"
85
+ "digest": "5176a2b292cab40a1b954134042c27c4a19077a999c9f13f9bc16d8dd007063b"
74
86
  },
75
87
  {
76
88
  "storage": "agents/skills/orkestrel-align-packages/SKILL.md",
@@ -124,7 +136,7 @@
124
136
  "storage": "agents/skills/orkestrel-debrief/references/field-testing.md",
125
137
  "destination": ".agents/skills/orkestrel-debrief/references/field-testing.md",
126
138
  "executable": false,
127
- "digest": "196f803242195df9cd9d2d32a3502ba22445877506f1573f12a428387808578f"
139
+ "digest": "986896359716e315f1e0859c7190fe459c5a0ea86c0ed772c9fa5e42bd6a8171"
128
140
  },
129
141
  {
130
142
  "storage": "agents/skills/orkestrel-debrief/references/instruction-audit.md",
@@ -160,7 +172,7 @@
160
172
  "storage": "agents/skills/orkestrel-falsify/references/reconcile.md",
161
173
  "destination": ".agents/skills/orkestrel-falsify/references/reconcile.md",
162
174
  "executable": false,
163
- "digest": "6880bc120f07bd1a20aa02596e550c3c5f5c66a241e9b40f7558ee144988db3b"
175
+ "digest": "fb3caa752883b151f83c95a2626ed11aebc653b07c3bf1baa1ace884f34b541f"
164
176
  },
165
177
  {
166
178
  "storage": "agents/skills/orkestrel-harden-package/SKILL.md",
@@ -262,7 +274,7 @@
262
274
  "storage": "agents/skills/orkestrel-publish/SKILL.md",
263
275
  "destination": ".agents/skills/orkestrel-publish/SKILL.md",
264
276
  "executable": false,
265
- "digest": "7c259c5945838d0fedfd6eaf453bd912f6af802e26e74aebac13810018218c8c"
277
+ "digest": "5256a9055a2dff5daf81c07354ab29dd85bd1f331634c1f6406eed45f3eae797"
266
278
  },
267
279
  {
268
280
  "storage": "agents/skills/orkestrel-publish/agents/openai.yaml",
@@ -274,13 +286,13 @@
274
286
  "storage": "agents/skills/orkestrel-publish/references/wave.md",
275
287
  "destination": ".agents/skills/orkestrel-publish/references/wave.md",
276
288
  "executable": false,
277
- "digest": "9f8dbb293bccffbc87751d70b828a8a7e6e6f09fb60ae9fc9f66aeb9f135e906"
289
+ "digest": "06ce2463d87c6fe9a36eeb304f4029758a4c1dc8f140fdbc1525b941bd243118"
278
290
  },
279
291
  {
280
292
  "storage": "agents/skills/orkestrel-publish/references/window.md",
281
293
  "destination": ".agents/skills/orkestrel-publish/references/window.md",
282
294
  "executable": false,
283
- "digest": "db102eb706708e887d8abf0d1f1bdd1132850e4dced735fb8c8378595c819ffd"
295
+ "digest": "c8a25826af1d3fce09eb39388cec0697faa926159515dc5351422546d80e6440"
284
296
  },
285
297
  {
286
298
  "storage": "agents/templates/brief.md",
@@ -340,7 +352,7 @@
340
352
  "storage": "claude/agents/orkestrel.md",
341
353
  "destination": ".claude/agents/orkestrel.md",
342
354
  "executable": false,
343
- "digest": "b5ad0097d6ec9d4ac23e3d42cba7cc4edfe45f091bf9f53a2c564c7184537fb9"
355
+ "digest": "b6c285b9923947460066ebc08f5778891ae2eac14800f40cdbcc53a63875554a"
344
356
  },
345
357
  {
346
358
  "storage": "claude/agents/planner.md",
@@ -358,7 +370,7 @@
358
370
  "storage": "claude/agents/reviewer.md",
359
371
  "destination": ".claude/agents/reviewer.md",
360
372
  "executable": false,
361
- "digest": "c2282266135e7797e778e0192a6a120d24137889a0150ecf4ce1a20df8013797"
373
+ "digest": "994e4fa7c889d1fe7dee2416215888fdb680e6a08a68178c513e29e558b0966c"
362
374
  },
363
375
  {
364
376
  "storage": "claude/agents/scout.md",
@@ -388,7 +400,7 @@
388
400
  "storage": "claude/rules/architecture.md",
389
401
  "destination": ".claude/rules/architecture.md",
390
402
  "executable": false,
391
- "digest": "2e479c2214b996901fca316d005117c5579f5064570b79bc47b99112dad569f3"
403
+ "digest": "2251fd693958c004e2a9e6087bf9248e43290694eb6488e3faa436a296d15320"
392
404
  },
393
405
  {
394
406
  "storage": "claude/rules/browser.md",
@@ -400,7 +412,7 @@
400
412
  "storage": "claude/rules/documentation.md",
401
413
  "destination": ".claude/rules/documentation.md",
402
414
  "executable": false,
403
- "digest": "db0f5f73f0f94a28ca7b6354290008a0634c50331be21084d8452b8496303fda"
415
+ "digest": "c974197ea9905f23606ad14ae993813f0a5a262dd703b96716561894d6e8a6e2"
404
416
  },
405
417
  {
406
418
  "storage": "claude/rules/names.md",
@@ -418,13 +430,13 @@
418
430
  "storage": "claude/rules/portability.md",
419
431
  "destination": ".claude/rules/portability.md",
420
432
  "executable": false,
421
- "digest": "b948c92e3409efd01b45ed9e8d917f4713cda0ecd5ea343932613b2977a614ff"
433
+ "digest": "ee7a2ae15ca0affa4e7176a3a123f01189e80de47c731a66fb7091c0c207b70e"
422
434
  },
423
435
  {
424
436
  "storage": "claude/rules/quality.md",
425
437
  "destination": ".claude/rules/quality.md",
426
438
  "executable": false,
427
- "digest": "e99ef9fca7a0224e19f657eb52301314624bd0364c21faebe3ae1f1e6428b72a"
439
+ "digest": "dc334e2d167f6688d78b79d8214e1635098559331700e5516215a7fec0d95c25"
428
440
  },
429
441
  {
430
442
  "storage": "claude/rules/styles.md",
@@ -436,37 +448,37 @@
436
448
  "storage": "claude/rules/tests.md",
437
449
  "destination": ".claude/rules/tests.md",
438
450
  "executable": false,
439
- "digest": "71d0795d722213c73a7561e91021be0b5c1287167e3b4a93eedfde262180d2f5"
451
+ "digest": "5ba1c577e6e86c6d35acd02c23339489b6c0a1c14c79249d0aab6b771d7ffac3"
440
452
  },
441
453
  {
442
454
  "storage": "claude/rules/typescript.md",
443
455
  "destination": ".claude/rules/typescript.md",
444
456
  "executable": false,
445
- "digest": "0427a66e6005eeb05a31ae4ee6cb1ec5b411ccf39183d14fe979d4728c391b01"
457
+ "digest": "5b8deb2fe595d350382d2af62f9c19c16730ff4bf91ba2dc6060e4de9ec40ab6"
446
458
  },
447
459
  {
448
460
  "storage": "claude/rules/workspace.md",
449
461
  "destination": ".claude/rules/workspace.md",
450
462
  "executable": false,
451
- "digest": "8120bac99d00b4cb0613b0dc6cb4048074c86fdb6f6583be07514229d90bbd07"
463
+ "digest": "5fbf4f0e9803ef1e4cbaa12a2cfb896cec1188f9c2fc3e020b93923319c91b2f"
452
464
  },
453
465
  {
454
466
  "storage": "claude/rules/writing.md",
455
467
  "destination": ".claude/rules/writing.md",
456
468
  "executable": false,
457
- "digest": "dc979ac7bbabf483c66b22f68460d0f03ef5c3059854f1a0802d8a2a9213d2f3"
469
+ "digest": "de30d71c93176c8216aa8f9792e669263104053106a76720af96bf77dea9a61a"
458
470
  },
459
471
  {
460
472
  "storage": "claude/settings.json",
461
473
  "destination": ".claude/settings.json",
462
474
  "executable": false,
463
- "digest": "c5c379bf5001754d11b94e692f7624e0a59c7e4b2b2072569b115c3723c24ee7"
475
+ "digest": "52c5aafe0f3a891a909a5ce26ad761abb38f2ab931f084f5ca147abb7680c419"
464
476
  },
465
477
  {
466
478
  "storage": "claude/skills/enterprise-bootstrap/SKILL.md",
467
479
  "destination": ".claude/skills/enterprise-bootstrap/SKILL.md",
468
480
  "executable": false,
469
- "digest": "1e2c83abb2a7c6a2e41dc210202d25124f6a30cfdc1bbd44d6b9c59dded6d93f"
481
+ "digest": "0973f97ccd042e53d33f72396fe1924b1de98f2a2c27338615279bb1d569e000"
470
482
  },
471
483
  {
472
484
  "storage": "claude/skills/orkestrel-align-packages/SKILL.md",
@@ -562,7 +574,7 @@
562
574
  "storage": "codex/agents/orkestrel.toml",
563
575
  "destination": ".codex/agents/orkestrel.toml",
564
576
  "executable": false,
565
- "digest": "5eeb3a09e67d406f01d163d3fc1411849623113bb361ae4d8d5771725ab1248d"
577
+ "digest": "e71a2259bfd90689d206021446a550bfd8f477951b4b760a4637ac40ff9da58c"
566
578
  },
567
579
  {
568
580
  "storage": "codex/agents/planner.toml",
@@ -580,7 +592,7 @@
580
592
  "storage": "codex/agents/reviewer.toml",
581
593
  "destination": ".codex/agents/reviewer.toml",
582
594
  "executable": false,
583
- "digest": "212fbf0df0fffe2e8077ab6e4c2b2c7ce944ed36d6a4eea274a49ec690c9a048"
595
+ "digest": "6002dcb596ebc7af910fc513c76f21f306082ac595cd164c20aa873995e6b844"
584
596
  },
585
597
  {
586
598
  "storage": "codex/agents/scout.toml",
@@ -604,13 +616,13 @@
604
616
  "storage": "configs/helpers.ts",
605
617
  "destination": "configs/helpers.ts",
606
618
  "executable": false,
607
- "digest": "cb43bf1dcd9aef3f806a3189ad74777366b59ff1f8e1a03868b02cae5919747f"
619
+ "digest": "85934473e279bbd955e23c7d8596db6c8798a1f44ed451506e7b3abd9fcfe9e1"
608
620
  },
609
621
  {
610
622
  "storage": "configs/policy.ts",
611
623
  "destination": "configs/policy.ts",
612
624
  "executable": false,
613
- "digest": "7f1176a024d1aa7041d8fdb58a2f80886e882686c074dd07278b5b0c42c27510"
625
+ "digest": "477235716dca10f84af294fa7ebad7ff4a321aafe563664b7cd24676f31459d9"
614
626
  },
615
627
  {
616
628
  "storage": "cursor/mcp.json",
@@ -664,7 +676,7 @@
664
676
  "storage": "dotfiles/oxlintrc.json",
665
677
  "destination": ".oxlintrc.json",
666
678
  "executable": false,
667
- "digest": "c2d9715f5db07992601cf764532c9d9c7b11177a366d46d767ce07f50f49acad"
679
+ "digest": "76489f915efe64f08b72709f4b499e145852444df9b1e94273f9dddd9d2af3a5"
668
680
  },
669
681
  {
670
682
  "storage": "dotfiles/prettierignore",
@@ -676,13 +688,13 @@
676
688
  "storage": "guides/guide.md",
677
689
  "destination": "guides/guide.md",
678
690
  "executable": false,
679
- "digest": "35ff2047e5eeb2d5efec9f6117675fd423762f9620870cb52f7781d42ac58ff7"
691
+ "digest": "3ae72c3f2d420a987c45ffbc94b3fe43f005b374d5bb3720c600751fb7197302"
680
692
  },
681
693
  {
682
694
  "storage": "guides/scaffold.md",
683
695
  "destination": "guides/scaffold.md",
684
696
  "executable": false,
685
- "digest": "3a97ee2ea9cbf5cc109930a18502b24b3baef3de37e1c22989bd1cc62311a6a1"
697
+ "digest": "3f26b6d6649a7fb5882c02d674c5c10386706e87a5305b863a0d2028a2bf5043"
686
698
  },
687
699
  {
688
700
  "storage": "scripts/codex.sh",
@@ -706,25 +718,25 @@
706
718
  "storage": "scripts/ollama.sh",
707
719
  "destination": "scripts/ollama.sh",
708
720
  "executable": true,
709
- "digest": "666979b75f02c7f550c3acc74a02b67594407b887e6a8b90f0ba38a0d79bec07"
721
+ "digest": "be5cb42991792190d7f61c7b8eef568eaf207db67cce5d2ccc223af814f2d6fa"
710
722
  },
711
723
  {
712
724
  "storage": "tests/config.test.ts",
713
725
  "destination": "tests/config.test.ts",
714
726
  "executable": false,
715
- "digest": "f8ddc2ab6cb54f2b1f51ad9212d7f09881271d265628299e5ce8f536cfec76a7"
727
+ "digest": "b880877803d909f83bc2ba4110d795cfecf620402696272e967bb55b90b66578"
716
728
  },
717
729
  {
718
730
  "storage": "tests/policy.test.ts",
719
731
  "destination": "tests/policy.test.ts",
720
732
  "executable": false,
721
- "digest": "f1f9ad779a090e9921695c743068025c387ccd610a09fee411eda809475a7367"
733
+ "digest": "75791e0758d4eb4a4acc954be7660790b92e1607ee783be9a741723b58c541d1"
722
734
  },
723
735
  {
724
736
  "storage": "tests/setupPolicy.ts",
725
737
  "destination": "tests/setupPolicy.ts",
726
738
  "executable": false,
727
- "digest": "e33cc2f49a5bc201e339f50c59d280d36d06a5b687bc080a00b41f09577fa7c7"
739
+ "digest": "ebe2777780193eed9d8d0641d6326535a6b5084d88aee0b6361d0b2409281ded"
728
740
  }
729
741
  ],
730
742
  "roots": [
@@ -770,7 +782,8 @@
770
782
  ".claude/skills/orkestrel-prove-journey",
771
783
  ".claude/skills/orkestrel-publish",
772
784
  ".codex/agents",
773
- ".cursor/rules"
785
+ ".cursor/rules",
786
+ "scripts"
774
787
  ],
775
- "digest": "b5e8423e828cb6d5f811692991de7dc6b5a347388229488a0461bff4e79e11dc"
788
+ "digest": "06692c3a078815c3d4e0ed00c4280ce381119a1c1005334b1372c137e758dc18"
776
789
  }
@@ -1,20 +1,329 @@
1
- #!/bin/bash
2
- # SessionStart loopback capability probe. Lifecycle stays user-owned and no
3
- # caller-controlled endpoint is contacted or copied into model context.
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
4
3
 
5
- if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
6
- exit 0
7
- fi
4
+ MAX_SECONDS=590
5
+ CLEANUP_SECONDS=5
6
+ OPERATION_SECONDS=$((MAX_SECONDS - CLEANUP_SECONDS - 1))
7
+ STARTED_SECONDS=$SECONDS
8
+ STARTUP_SECONDS=60
9
+ INSTALLER_URL='https://ollama.com/install.sh'
10
+
11
+ launched_pid=
12
+ installer_path=
13
+
14
+ fail() {
15
+ printf 'ollama.sh: %s\n' "$1" >&2
16
+ exit 1
17
+ }
18
+
19
+ remaining_seconds() {
20
+ local remaining=$((OPERATION_SECONDS - (SECONDS - STARTED_SECONDS)))
21
+ if ((remaining < 1)); then
22
+ printf '0\n'
23
+ else
24
+ printf '%s\n' "$remaining"
25
+ fi
26
+ }
27
+
28
+ terminate_group() {
29
+ local pid=$1
30
+ local deadline=$((SECONDS + CLEANUP_SECONDS))
31
+ kill -TERM -- "-$pid" >/dev/null 2>&1 || true
32
+ while kill -0 -- "-$pid" >/dev/null 2>&1; do
33
+ if ((SECONDS >= deadline)); then
34
+ kill -KILL -- "-$pid" >/dev/null 2>&1 || true
35
+ break
36
+ fi
37
+ sleep 1
38
+ done
39
+ wait "$pid" >/dev/null 2>&1 || true
40
+ }
41
+
42
+ cleanup() {
43
+ local status=$1
44
+ if [[ -n "$installer_path" && -f "$installer_path" ]]; then
45
+ rm -f -- "$installer_path"
46
+ fi
47
+ if ((status != 0)) && [[ -n "$launched_pid" ]]; then
48
+ terminate_group "$launched_pid"
49
+ fi
50
+ exit "$status"
51
+ }
52
+
53
+ normalize_host() {
54
+ OLLAMA_INPUT="$1" "$node_path" -e '
55
+ const { isIP } = require("node:net")
56
+ const raw = process.env.OLLAMA_INPUT
57
+ if (raw === undefined || raw.length === 0) process.exit(1)
58
+ let url
59
+ try {
60
+ url = new URL(/^https?:\/\//iu.test(raw) ? raw : `http://${raw}`)
61
+ } catch {
62
+ process.exit(1)
63
+ }
64
+ if (
65
+ (url.protocol !== "http:" && url.protocol !== "https:") ||
66
+ url.username !== "" ||
67
+ url.password !== "" ||
68
+ url.pathname !== "/" ||
69
+ url.search !== "" ||
70
+ url.hash !== "" ||
71
+ url.hostname === ""
72
+ ) process.exit(1)
73
+ const hostname =
74
+ url.hostname.startsWith("[") && url.hostname.endsWith("]")
75
+ ? url.hostname.slice(1, -1)
76
+ : url.hostname
77
+ const family = isIP(hostname)
78
+ const loopback =
79
+ hostname === "localhost" ||
80
+ (family === 4 && hostname.split(".")[0] === "127") ||
81
+ (family === 6 && hostname === "::1")
82
+ process.stdout.write(`${url.origin}\n${loopback ? "true" : "false"}`)
83
+ '
84
+ }
85
+
86
+ build_payload() {
87
+ OLLAMA_MODEL_VALUE="$model" "$node_path" -e '
88
+ const model = process.env.OLLAMA_MODEL_VALUE
89
+ if (model === undefined || model.length === 0) process.exit(1)
90
+ let body
91
+ switch (process.argv[1]) {
92
+ case "show":
93
+ body = { model }
94
+ break
95
+ case "pull":
96
+ body = { model, stream: false }
97
+ break
98
+ case "chat":
99
+ body = {
100
+ model,
101
+ messages: [{ role: "user", content: "hi" }],
102
+ stream: false,
103
+ think: false,
104
+ keep_alive: "30m",
105
+ options: { num_predict: 1 },
106
+ }
107
+ break
108
+ default:
109
+ process.exit(1)
110
+ }
111
+ process.stdout.write(JSON.stringify(body))
112
+ ' "$1"
113
+ }
114
+
115
+ validate_response() {
116
+ "$node_path" -e '
117
+ const { readFileSync } = require("node:fs")
118
+ let body
119
+ try {
120
+ body = JSON.parse(readFileSync(0, "utf8"))
121
+ } catch {
122
+ process.exit(1)
123
+ }
124
+ if (body === null || typeof body !== "object" || Array.isArray(body)) process.exit(1)
125
+ if (process.argv[1] === "pull") process.exit(body.status === "success" ? 0 : 1)
126
+ if (process.argv[1] === "chat") process.exit(body.done === true ? 0 : 1)
127
+ process.exit(1)
128
+ ' "$1"
129
+ }
8
130
 
9
- if ! command -v ollama >/dev/null 2>&1; then
10
- echo "ollama.sh: loopback capability unavailable."
11
- exit 0
131
+ probe_version() {
132
+ local allowance=$1
133
+ local status
134
+ if ! status=$(curl --silent --max-time "$allowance" --output /dev/null \
135
+ --write-out '%{http_code}' "$normalized_host/api/version" 2>/dev/null); then
136
+ return 1
137
+ fi
138
+ [[ "$status" =~ ^2[0-9][0-9]$ ]]
139
+ }
140
+
141
+ trap 'cleanup $?' EXIT
142
+
143
+ if ! command -v node >/dev/null 2>&1; then
144
+ printf 'ollama.sh: node is required for URL and JSON handling\n' >&2
145
+ exit 127
12
146
  fi
147
+ if ! command -v curl >/dev/null 2>&1; then
148
+ printf 'ollama.sh: curl is required for Ollama setup\n' >&2
149
+ exit 127
150
+ fi
151
+
152
+ node_path=$(command -v node)
153
+ script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
154
+ project_root=$(cd -- "$script_dir/.." && pwd -P)
155
+ tmp_dir="$project_root/tmp"
156
+ mkdir -p -- "$tmp_dir"
13
157
 
14
- if curl -sf --max-time 2 "http://127.0.0.1:11434/api/tags" >/dev/null 2>&1; then
15
- echo "ollama.sh: loopback capability ready; lifecycle remains user-owned."
158
+ if [[ ${OLLAMA_HOST+x} == x ]]; then
159
+ host_input=$OLLAMA_HOST
16
160
  else
17
- echo "ollama.sh: loopback capability unreachable; lifecycle remains user-owned."
161
+ host_input='http://127.0.0.1:11434'
162
+ fi
163
+ if [[ ${OLLAMA_MODEL+x} == x ]]; then
164
+ model=$OLLAMA_MODEL
165
+ else
166
+ model='qwen3.5:2b-q4_K_M'
167
+ fi
168
+ if [[ -z "$model" ]]; then
169
+ fail 'OLLAMA_MODEL must not be empty'
170
+ fi
171
+
172
+ if ! host_data=$(normalize_host "$host_input"); then
173
+ fail 'OLLAMA_HOST must be an HTTP origin without credentials, path, query, or fragment'
174
+ fi
175
+ if [[ "$host_data" != *$'\n'* ]]; then
176
+ fail 'OLLAMA_HOST normalization returned no endpoint classification'
177
+ fi
178
+ normalized_host=${host_data%%$'\n'*}
179
+ loopback=${host_data#*$'\n'}
180
+
181
+ ready=false
182
+ if probe_version 2; then
183
+ ready=true
184
+ fi
185
+
186
+ if [[ "$ready" != true ]]; then
187
+ if [[ "$loopback" != true || "$normalized_host" != http://* ]]; then
188
+ fail 'the configured endpoint is unreachable; local startup is limited to HTTP loopback'
189
+ fi
190
+
191
+ if ! command -v ollama >/dev/null 2>&1; then
192
+ if [[ ${CLAUDE_CODE_REMOTE:-} != true && ${CI:-} != true ]]; then
193
+ printf 'ollama.sh: ollama is required to start an unreachable loopback endpoint\n' >&2
194
+ exit 127
195
+ fi
196
+ if [[ $(uname -s) != Linux ]]; then
197
+ fail 'automatic Ollama installation is supported only on Linux'
198
+ fi
199
+ if ! command -v timeout >/dev/null 2>&1; then
200
+ printf 'ollama.sh: timeout is required to bound automatic installation\n' >&2
201
+ exit 127
202
+ fi
203
+ if ! command -v mktemp >/dev/null 2>&1; then
204
+ printf 'ollama.sh: mktemp is required for automatic installation\n' >&2
205
+ exit 127
206
+ fi
207
+ installer_path=$(mktemp "$tmp_dir/ollama-install.XXXXXX.sh")
208
+ remaining=$(remaining_seconds)
209
+ if ((remaining == 0)); then
210
+ fail 'the setup deadline expired before installation'
211
+ fi
212
+ if ! curl --fail --silent --show-error --location \
213
+ --proto '=https' --proto-redir '=https' --max-time "$remaining" \
214
+ --output "$installer_path" "$INSTALLER_URL"; then
215
+ fail 'the official Ollama installer download failed'
216
+ fi
217
+ if [[ ! -s "$installer_path" ]]; then
218
+ fail 'the official Ollama installer download was empty'
219
+ fi
220
+ remaining=$(remaining_seconds)
221
+ if ((remaining == 0)); then
222
+ fail 'the setup deadline expired before installation'
223
+ fi
224
+ if ! timeout --signal=TERM --kill-after=5s "${remaining}s" sh "$installer_path"; then
225
+ fail 'the official Ollama installer did not complete within the setup deadline'
226
+ fi
227
+ ollama_path=$(command -v ollama || true)
228
+ if [[ -z "$ollama_path" || ! -f "$ollama_path" || ! -x "$ollama_path" ]]; then
229
+ fail 'the official Ollama installer completed without an executable in PATH'
230
+ fi
231
+ remaining=$(remaining_seconds)
232
+ if ((remaining == 0)); then
233
+ fail 'the setup deadline expired after installation'
234
+ fi
235
+ probe_allowance=2
236
+ if ((remaining < probe_allowance)); then probe_allowance=$remaining; fi
237
+ if probe_version "$probe_allowance"; then
238
+ ready=true
239
+ fi
240
+ fi
241
+
242
+ if [[ "$ready" != true ]]; then
243
+ host_kernel=$(uname -s)
244
+ if [[ "$host_kernel" == MINGW* || "$host_kernel" == MSYS* || "$host_kernel" == CYGWIN* ]]; then
245
+ fail 'starting Ollama from Git Bash is unsupported because failure cleanup cannot terminate its Windows process tree safely'
246
+ fi
247
+ if ! command -v setsid >/dev/null 2>&1; then
248
+ fail 'setsid is required to own an Ollama daemon process group for failure cleanup'
249
+ fi
250
+ ollama_path=$(command -v ollama)
251
+ log_path="$tmp_dir/ollama-service.log"
252
+ OLLAMA_HOST="$normalized_host" setsid "$ollama_path" serve </dev/null >>"$log_path" 2>&1 &
253
+ launched_pid=$!
254
+ startup_started=$SECONDS
255
+ while true; do
256
+ remaining=$(remaining_seconds)
257
+ startup_remaining=$((STARTUP_SECONDS - (SECONDS - startup_started)))
258
+ if ((remaining == 0 || startup_remaining <= 0)); then
259
+ fail 'Ollama did not become ready within the startup deadline; see tmp/ollama-service.log'
260
+ fi
261
+ probe_allowance=2
262
+ if ((remaining < probe_allowance)); then probe_allowance=$remaining; fi
263
+ if ((startup_remaining < probe_allowance)); then probe_allowance=$startup_remaining; fi
264
+ if probe_version "$probe_allowance"; then
265
+ ready=true
266
+ break
267
+ fi
268
+ sleep 1
269
+ done
270
+ fi
271
+ fi
272
+
273
+ show_payload=$(build_payload show) || fail 'failed to serialize the model request'
274
+ remaining=$(remaining_seconds)
275
+ if ((remaining == 0)); then fail 'the setup deadline expired before model inspection'; fi
276
+ if ! show_status=$(curl --silent --show-error --max-time "$remaining" \
277
+ --output /dev/null --write-out '%{http_code}' \
278
+ --header 'Content-Type: application/json' --data-binary "$show_payload" \
279
+ "$normalized_host/api/show"); then
280
+ fail 'the model inspection request failed'
281
+ fi
282
+
283
+ if [[ "$show_status" == 404 ]]; then
284
+ pull_payload=$(build_payload pull) || fail 'failed to serialize the pull request'
285
+ remaining=$(remaining_seconds)
286
+ if ((remaining == 0)); then fail 'the setup deadline expired before model pull'; fi
287
+ if ! pull_wire=$(curl --silent --show-error --max-time "$remaining" \
288
+ --write-out $'\n%{http_code}' \
289
+ --header 'Content-Type: application/json' --data-binary "$pull_payload" \
290
+ "$normalized_host/api/pull"); then
291
+ fail 'the model pull request failed'
292
+ fi
293
+ if [[ "$pull_wire" != *$'\n'* ]]; then
294
+ fail 'the model pull response carried no HTTP status'
295
+ fi
296
+ pull_status=${pull_wire##*$'\n'}
297
+ pull_response=${pull_wire%$'\n'*}
298
+ if [[ ! "$pull_status" =~ ^2[0-9][0-9]$ ]]; then
299
+ fail 'the model pull request returned an unsuccessful HTTP status'
300
+ fi
301
+ if ! printf '%s' "$pull_response" | validate_response pull; then
302
+ fail 'the model pull response did not report completion'
303
+ fi
304
+ elif [[ ! "$show_status" =~ ^2[0-9][0-9]$ ]]; then
305
+ fail 'the model inspection request failed without reporting absence'
306
+ fi
307
+
308
+ chat_payload=$(build_payload chat) || fail 'failed to serialize the warm request'
309
+ remaining=$(remaining_seconds)
310
+ if ((remaining == 0)); then fail 'the setup deadline expired before model warmup'; fi
311
+ if ! chat_wire=$(curl --silent --show-error --max-time "$remaining" \
312
+ --write-out $'\n%{http_code}' \
313
+ --header 'Content-Type: application/json' --data-binary "$chat_payload" \
314
+ "$normalized_host/api/chat"); then
315
+ fail 'the model warm request failed'
316
+ fi
317
+ if [[ "$chat_wire" != *$'\n'* ]]; then
318
+ fail 'the model warm response carried no HTTP status'
319
+ fi
320
+ chat_status=${chat_wire##*$'\n'}
321
+ chat_response=${chat_wire%$'\n'*}
322
+ if [[ ! "$chat_status" =~ ^2[0-9][0-9]$ ]]; then
323
+ fail 'the model warm request returned an unsuccessful HTTP status'
324
+ fi
325
+ if ! printf '%s' "$chat_response" | validate_response chat; then
326
+ fail 'the model warm response did not report completion'
18
327
  fi
19
328
 
20
- exit 0
329
+ printf 'ollama.sh: Ollama is ready\n'