@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.
- package/README.md +29 -104
- package/dist/bin/main.js +95 -27
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +2 -2
- package/dist/host/CLAUDE.md +6 -0
- package/dist/host/agents/orchestration.md +23 -15
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +184 -177
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +314 -91
- package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +241 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +83 -36
- package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +297 -98
- package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +25 -14
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +216 -128
- package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +187 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +109 -20
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
- package/dist/host/claude/agents/orkestrel.md +56 -56
- package/dist/host/claude/agents/reviewer.md +13 -0
- package/dist/host/claude/rules/architecture.md +51 -45
- package/dist/host/claude/rules/documentation.md +18 -1
- package/dist/host/claude/rules/portability.md +2 -0
- package/dist/host/claude/rules/quality.md +1 -1
- package/dist/host/claude/rules/tests.md +12 -11
- package/dist/host/claude/rules/typescript.md +5 -0
- package/dist/host/claude/rules/workspace.md +25 -20
- package/dist/host/claude/rules/writing.md +4 -0
- package/dist/host/claude/settings.json +1 -1
- package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -9
- package/dist/host/codex/agents/orkestrel.toml +3 -3
- package/dist/host/codex/agents/reviewer.toml +4 -2
- package/dist/host/configs/helpers.ts +311 -2
- package/dist/host/configs/policy.ts +1100 -51
- package/dist/host/dotfiles/oxlintrc.json +72 -1
- package/dist/host/guides/guide.md +749 -222
- package/dist/host/guides/scaffold.md +529 -394
- package/dist/host/manifest.json +53 -40
- package/dist/host/scripts/ollama.sh +322 -13
- package/dist/host/tests/config.test.ts +1200 -16
- package/dist/host/tests/policy.test.ts +157 -173
- package/dist/host/tests/setupPolicy.ts +522 -1007
- package/dist/src/core/index.cjs +402 -287
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +160 -128
- package/dist/src/core/index.d.ts +160 -128
- package/dist/src/core/index.js +400 -286
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +28 -21
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +38 -33
- package/dist/src/server/index.d.ts +38 -33
- package/dist/src/server/index.js +28 -21
- package/dist/src/server/index.js.map +1 -1
- package/package.json +18 -19
package/dist/host/manifest.json
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
"storage": "AGENTS.md",
|
|
5
5
|
"destination": "AGENTS.md",
|
|
6
6
|
"executable": false,
|
|
7
|
-
"digest": "
|
|
7
|
+
"digest": "365f62b880fac923347c8e78482bc8130559a15e5434e4aeb5cbd46b853ea6f6"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"storage": "CLAUDE.md",
|
|
11
11
|
"destination": "CLAUDE.md",
|
|
12
12
|
"executable": false,
|
|
13
|
-
"digest": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
788
|
+
"digest": "06692c3a078815c3d4e0ed00c4280ce381119a1c1005334b1372c137e758dc18"
|
|
776
789
|
}
|
|
@@ -1,20 +1,329 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# caller-controlled endpoint is contacted or copied into model context.
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
15
|
-
|
|
158
|
+
if [[ ${OLLAMA_HOST+x} == x ]]; then
|
|
159
|
+
host_input=$OLLAMA_HOST
|
|
16
160
|
else
|
|
17
|
-
|
|
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
|
-
|
|
329
|
+
printf 'ollama.sh: Ollama is ready\n'
|