freelang-v4 4.3.0

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 (261) hide show
  1. package/README.md +548 -0
  2. package/dist/ast.d.ts +367 -0
  3. package/dist/ast.js +4 -0
  4. package/dist/ast.js.map +1 -0
  5. package/dist/async-basic.test.d.ts +1 -0
  6. package/dist/async-basic.test.js +88 -0
  7. package/dist/async-basic.test.js.map +1 -0
  8. package/dist/async-jest.test.d.ts +1 -0
  9. package/dist/async-jest.test.js +99 -0
  10. package/dist/async-jest.test.js.map +1 -0
  11. package/dist/channel-jest.test.d.ts +1 -0
  12. package/dist/channel-jest.test.js +148 -0
  13. package/dist/channel-jest.test.js.map +1 -0
  14. package/dist/checker-jest.test.d.ts +1 -0
  15. package/dist/checker-jest.test.js +160 -0
  16. package/dist/checker-jest.test.js.map +1 -0
  17. package/dist/checker.d.ts +149 -0
  18. package/dist/checker.js +1565 -0
  19. package/dist/checker.js.map +1 -0
  20. package/dist/checker.test.d.ts +1 -0
  21. package/dist/checker.test.js +217 -0
  22. package/dist/checker.test.js.map +1 -0
  23. package/dist/compiler-jest.test.d.ts +1 -0
  24. package/dist/compiler-jest.test.js +233 -0
  25. package/dist/compiler-jest.test.js.map +1 -0
  26. package/dist/compiler.d.ts +127 -0
  27. package/dist/compiler.js +1588 -0
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/compiler.test.d.ts +1 -0
  30. package/dist/compiler.test.js +313 -0
  31. package/dist/compiler.test.js.map +1 -0
  32. package/dist/db-100m-full.d.ts +5 -0
  33. package/dist/db-100m-full.js +78 -0
  34. package/dist/db-100m-full.js.map +1 -0
  35. package/dist/db-100m-no-index.d.ts +12 -0
  36. package/dist/db-100m-no-index.js +119 -0
  37. package/dist/db-100m-no-index.js.map +1 -0
  38. package/dist/db-100m-real.d.ts +5 -0
  39. package/dist/db-100m-real.js +131 -0
  40. package/dist/db-100m-real.js.map +1 -0
  41. package/dist/db-100m-streaming.d.ts +15 -0
  42. package/dist/db-100m-streaming.js +164 -0
  43. package/dist/db-100m-streaming.js.map +1 -0
  44. package/dist/db-100m-test.d.ts +5 -0
  45. package/dist/db-100m-test.js +111 -0
  46. package/dist/db-100m-test.js.map +1 -0
  47. package/dist/db-jest.test.d.ts +1 -0
  48. package/dist/db-jest.test.js +182 -0
  49. package/dist/db-jest.test.js.map +1 -0
  50. package/dist/db-runtime.d.ts +24 -0
  51. package/dist/db-runtime.js +204 -0
  52. package/dist/db-runtime.js.map +1 -0
  53. package/dist/db.d.ts +249 -0
  54. package/dist/db.js +593 -0
  55. package/dist/db.js.map +1 -0
  56. package/dist/file-io-jest.test.d.ts +1 -0
  57. package/dist/file-io-jest.test.js +225 -0
  58. package/dist/file-io-jest.test.js.map +1 -0
  59. package/dist/for-of-jest.test.d.ts +1 -0
  60. package/dist/for-of-jest.test.js +230 -0
  61. package/dist/for-of-jest.test.js.map +1 -0
  62. package/dist/for-of.test.d.ts +1 -0
  63. package/dist/for-of.test.js +305 -0
  64. package/dist/for-of.test.js.map +1 -0
  65. package/dist/function-literal-jest.test.d.ts +1 -0
  66. package/dist/function-literal-jest.test.js +180 -0
  67. package/dist/function-literal-jest.test.js.map +1 -0
  68. package/dist/function-literal.test.d.ts +1 -0
  69. package/dist/function-literal.test.js +245 -0
  70. package/dist/function-literal.test.js.map +1 -0
  71. package/dist/generics-jest.test.d.ts +1 -0
  72. package/dist/generics-jest.test.js +93 -0
  73. package/dist/generics-jest.test.js.map +1 -0
  74. package/dist/ir-gen.d.ts +15 -0
  75. package/dist/ir-gen.js +400 -0
  76. package/dist/ir-gen.js.map +1 -0
  77. package/dist/ir.d.ts +114 -0
  78. package/dist/ir.js +5 -0
  79. package/dist/ir.js.map +1 -0
  80. package/dist/lexer.d.ts +110 -0
  81. package/dist/lexer.js +467 -0
  82. package/dist/lexer.js.map +1 -0
  83. package/dist/lexer.test.d.ts +1 -0
  84. package/dist/lexer.test.js +426 -0
  85. package/dist/lexer.test.js.map +1 -0
  86. package/dist/main.d.ts +2 -0
  87. package/dist/main.js +241 -0
  88. package/dist/main.js.map +1 -0
  89. package/dist/module-jest.test.d.ts +1 -0
  90. package/dist/module-jest.test.js +123 -0
  91. package/dist/module-jest.test.js.map +1 -0
  92. package/dist/parser.d.ts +56 -0
  93. package/dist/parser.js +1060 -0
  94. package/dist/parser.js.map +1 -0
  95. package/dist/parser.test.d.ts +1 -0
  96. package/dist/parser.test.js +461 -0
  97. package/dist/parser.test.js.map +1 -0
  98. package/dist/pattern-matching-jest.test.d.ts +1 -0
  99. package/dist/pattern-matching-jest.test.js +158 -0
  100. package/dist/pattern-matching-jest.test.js.map +1 -0
  101. package/dist/pkg/init.d.ts +1 -0
  102. package/dist/pkg/init.js +118 -0
  103. package/dist/pkg/init.js.map +1 -0
  104. package/dist/pkg/install.d.ts +1 -0
  105. package/dist/pkg/install.js +77 -0
  106. package/dist/pkg/install.js.map +1 -0
  107. package/dist/pkg/registry.d.ts +23 -0
  108. package/dist/pkg/registry.js +106 -0
  109. package/dist/pkg/registry.js.map +1 -0
  110. package/dist/pkg/run.d.ts +1 -0
  111. package/dist/pkg/run.js +76 -0
  112. package/dist/pkg/run.js.map +1 -0
  113. package/dist/pkg/toml.d.ts +5 -0
  114. package/dist/pkg/toml.js +117 -0
  115. package/dist/pkg/toml.js.map +1 -0
  116. package/dist/repl.d.ts +15 -0
  117. package/dist/repl.js +197 -0
  118. package/dist/repl.js.map +1 -0
  119. package/dist/runtime/bytecode.d.ts +92 -0
  120. package/dist/runtime/bytecode.js +253 -0
  121. package/dist/runtime/bytecode.js.map +1 -0
  122. package/dist/runtime/value.d.ts +102 -0
  123. package/dist/runtime/value.js +302 -0
  124. package/dist/runtime/value.js.map +1 -0
  125. package/dist/runtime/vm.d.ts +65 -0
  126. package/dist/runtime/vm.js +293 -0
  127. package/dist/runtime/vm.js.map +1 -0
  128. package/dist/struct-instance-jest.test.d.ts +1 -0
  129. package/dist/struct-instance-jest.test.js +209 -0
  130. package/dist/struct-instance-jest.test.js.map +1 -0
  131. package/dist/struct-instance.test.d.ts +1 -0
  132. package/dist/struct-instance.test.js +291 -0
  133. package/dist/struct-instance.test.js.map +1 -0
  134. package/dist/struct-jest.test.d.ts +1 -0
  135. package/dist/struct-jest.test.js +176 -0
  136. package/dist/struct-jest.test.js.map +1 -0
  137. package/dist/struct.test.d.ts +1 -0
  138. package/dist/struct.test.js +231 -0
  139. package/dist/struct.test.js.map +1 -0
  140. package/dist/trait-jest.test.d.ts +1 -0
  141. package/dist/trait-jest.test.js +120 -0
  142. package/dist/trait-jest.test.js.map +1 -0
  143. package/dist/vm-jest.test.d.ts +1 -0
  144. package/dist/vm-jest.test.js +569 -0
  145. package/dist/vm-jest.test.js.map +1 -0
  146. package/dist/vm.d.ts +81 -0
  147. package/dist/vm.js +1956 -0
  148. package/dist/vm.js.map +1 -0
  149. package/dist/vm.test.d.ts +1 -0
  150. package/dist/vm.test.js +337 -0
  151. package/dist/vm.test.js.map +1 -0
  152. package/dist/web-repl/sandbox.d.ts +11 -0
  153. package/dist/web-repl/sandbox.js +76 -0
  154. package/dist/web-repl/sandbox.js.map +1 -0
  155. package/dist/web-repl/server.d.ts +1 -0
  156. package/dist/web-repl/server.js +111 -0
  157. package/dist/web-repl/server.js.map +1 -0
  158. package/dist/while-loop-jest.test.d.ts +1 -0
  159. package/dist/while-loop-jest.test.js +201 -0
  160. package/dist/while-loop-jest.test.js.map +1 -0
  161. package/dist/while-loop.test.d.ts +1 -0
  162. package/dist/while-loop.test.js +262 -0
  163. package/dist/while-loop.test.js.map +1 -0
  164. package/docs/EXPERIENCE.md +787 -0
  165. package/docs/README.md +175 -0
  166. package/docs/V1_V2_V3_ANALYSIS.md +107 -0
  167. package/docs/_config.yml +36 -0
  168. package/docs/api-reference.md +459 -0
  169. package/docs/architecture.md +470 -0
  170. package/docs/benchmarks.md +295 -0
  171. package/docs/comparison.md +454 -0
  172. package/docs/index.md +335 -0
  173. package/docs/language-completeness.md +228 -0
  174. package/docs/learning-guide.md +651 -0
  175. package/package.json +65 -0
  176. package/src/api/deploy_key.fl +294 -0
  177. package/src/api/issue.fl +302 -0
  178. package/src/api/org.fl +356 -0
  179. package/src/api/repo.fl +394 -0
  180. package/src/api/team.fl +299 -0
  181. package/src/api/user.fl +385 -0
  182. package/src/api/webhook.fl +273 -0
  183. package/src/ast.ts +158 -0
  184. package/src/async-basic.test.ts +94 -0
  185. package/src/async-jest.test.ts +107 -0
  186. package/src/channel-jest.test.ts +158 -0
  187. package/src/checker-jest.test.ts +189 -0
  188. package/src/checker.test.ts +279 -0
  189. package/src/checker.ts +1861 -0
  190. package/src/commands/analyze.fl +227 -0
  191. package/src/commands/auth.fl +315 -0
  192. package/src/commands/batch.fl +349 -0
  193. package/src/commands/config.fl +199 -0
  194. package/src/commands/deploy_key.fl +352 -0
  195. package/src/commands/issue.fl +275 -0
  196. package/src/commands/main.fl +492 -0
  197. package/src/commands/org.fl +425 -0
  198. package/src/commands/repo.fl +581 -0
  199. package/src/commands/team.fl +244 -0
  200. package/src/commands/user.fl +423 -0
  201. package/src/commands/webhook.fl +400 -0
  202. package/src/compiler-jest.test.ts +275 -0
  203. package/src/compiler.test.ts +375 -0
  204. package/src/compiler.ts +1770 -0
  205. package/src/config.fl +175 -0
  206. package/src/core/batch.fl +355 -0
  207. package/src/core/cache.fl +284 -0
  208. package/src/core/ensure.fl +324 -0
  209. package/src/db-100m-full.ts +96 -0
  210. package/src/db-100m-no-index.ts +133 -0
  211. package/src/db-100m-real.ts +152 -0
  212. package/src/db-100m-streaming.ts +154 -0
  213. package/src/db-100m-test.ts +136 -0
  214. package/src/db-jest.test.ts +161 -0
  215. package/src/db-runtime.ts +242 -0
  216. package/src/db.ts +676 -0
  217. package/src/errors.fl +134 -0
  218. package/src/for-of-jest.test.ts +246 -0
  219. package/src/for-of.test.ts +308 -0
  220. package/src/function-literal-jest.test.ts +193 -0
  221. package/src/function-literal.test.ts +248 -0
  222. package/src/generics-jest.test.ts +104 -0
  223. package/src/http/client.fl +327 -0
  224. package/src/ir-gen.ts +459 -0
  225. package/src/ir.ts +80 -0
  226. package/src/lexer.test.ts +499 -0
  227. package/src/lexer.ts +522 -0
  228. package/src/main.ts +223 -0
  229. package/src/models.fl +162 -0
  230. package/src/module-jest.test.ts +145 -0
  231. package/src/parser.test.ts +542 -0
  232. package/src/parser.ts +1211 -0
  233. package/src/pattern-matching-jest.test.ts +170 -0
  234. package/src/pkg/init.ts +91 -0
  235. package/src/pkg/install.ts +56 -0
  236. package/src/pkg/registry.ts +103 -0
  237. package/src/pkg/run.ts +49 -0
  238. package/src/pkg/toml.ts +129 -0
  239. package/src/repl.ts +190 -0
  240. package/src/runtime/bytecode.ts +291 -0
  241. package/src/runtime/value.ts +322 -0
  242. package/src/runtime/vm.ts +354 -0
  243. package/src/self-host/bootstrap.fl +68 -0
  244. package/src/self-host/interpreter.fl +361 -0
  245. package/src/self-host/lexer-simple.fl +22 -0
  246. package/src/self-host/lexer.fl +305 -0
  247. package/src/self-host/parser.fl +580 -0
  248. package/src/struct-instance-jest.test.ts +221 -0
  249. package/src/struct-instance.test.ts +293 -0
  250. package/src/struct-jest.test.ts +187 -0
  251. package/src/struct.test.ts +234 -0
  252. package/src/trait-jest.test.ts +136 -0
  253. package/src/vm-jest.test.ts +754 -0
  254. package/src/vm.ts +1976 -0
  255. package/src/web-repl/public/index.html +50 -0
  256. package/src/web-repl/public/main.js +105 -0
  257. package/src/web-repl/public/style.css +225 -0
  258. package/src/web-repl/sandbox.ts +88 -0
  259. package/src/web-repl/server.ts +97 -0
  260. package/src/while-loop-jest.test.ts +218 -0
  261. package/src/while-loop.test.ts +267 -0
@@ -0,0 +1,227 @@
1
+ // FreeLang v4.2 — gogs-cli Phase 5 분석 명령어
2
+ // analyze 구현 (저장소 통계, public/private 분포 등)
3
+ // 150라인
4
+
5
+ /// 서버 데이터 분석
6
+ /// gogs analyze [--json] [--yaml]
7
+ async fn cmd_analyze(args: CliArgs) -> CliResult {
8
+ // 1. 설정 로드
9
+ var host = await get_default_host()
10
+ if host.url == "" {
11
+ return CliResult {
12
+ exit_code: 1,
13
+ output: "",
14
+ error: "Not logged in"
15
+ }
16
+ }
17
+
18
+ // 2. 클라이언트 및 캐시
19
+ var client = new_client(host.url, host.token)
20
+ var cache = new_cache(0) // 분석은 캐시 안 함
21
+
22
+ // 3. 데이터 수집
23
+ println("Analyzing server data...")
24
+
25
+ var repo_service = new_repo_service(client, cache)
26
+ var repos_result = await list_repos(repo_service)
27
+
28
+ match repos_result {
29
+ Result::Ok(repos) => {
30
+ // 4. 분석 수행
31
+ var stats = analyze_repos(repos)
32
+
33
+ // 5. 포맷 선택
34
+ var format = get_format(args)
35
+
36
+ if format == "json" {
37
+ return analyze_json(stats)
38
+ }
39
+ if format == "yaml" {
40
+ return analyze_yaml(stats)
41
+ }
42
+
43
+ // 기본: 텍스트
44
+ analyze_text(stats)
45
+ }
46
+ Result::Err(err) => {
47
+ CliResult {
48
+ exit_code: 1,
49
+ output: "",
50
+ error: error_message(err)
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ // ==================== 분석 함수 ====================
57
+
58
+ /// 저장소 분석
59
+ fn analyze_repos(repos: [Repo]) -> RepositoryStats {
60
+ var public_count: i32 = 0
61
+ var private_count: i32 = 0
62
+ var recently_updated: [Repo] = []
63
+
64
+ var i: i32 = 0
65
+ while i < length(repos) {
66
+ var repo = repos[i]
67
+
68
+ if repo.private {
69
+ private_count = private_count + 1
70
+ } else {
71
+ public_count = public_count + 1
72
+ }
73
+
74
+ // 최근 업데이트된 저장소 수집 (7일 이내)
75
+ // TODO: 날짜 비교 구현
76
+ // if is_recently_updated(repo.updated_at) {
77
+ // recently_updated.push(repo)
78
+ // }
79
+
80
+ i = i + 1
81
+ }
82
+
83
+ // 최근 업데이트 저장소 정렬 (최신순)
84
+ var sorted = sort_repos_by_date(recently_updated)
85
+
86
+ RepositoryStats {
87
+ total: length(repos),
88
+ public: public_count,
89
+ private: private_count,
90
+ recently_updated_7days: length(sorted),
91
+ by_updated: sorted
92
+ }
93
+ }
94
+
95
+ /// 저장소 정렬 (날짜순)
96
+ fn sort_repos_by_date(repos: [Repo]) -> [Repo] {
97
+ // TODO: 정렬 알고리즘 구현 (버블 정렬 또는 병합 정렬)
98
+ repos // placeholder
99
+ }
100
+
101
+ /// 저장소가 최근에 업데이트되었는지 확인
102
+ fn is_recently_updated(updated_at: str) -> bool {
103
+ // TODO: 날짜 파싱 및 비교 구현
104
+ false // placeholder
105
+ }
106
+
107
+ // ==================== 출력 포맷 ====================
108
+
109
+ /// 텍스트 포맷 분석 결과
110
+ fn analyze_text(stats: RepositoryStats) -> CliResult {
111
+ var output = ""
112
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
113
+ output = output + "║ Repository Analysis ║\n"
114
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
115
+ output = output + "\n"
116
+
117
+ // 요약 통계
118
+ output = output + "📊 Summary\n"
119
+ output = output + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
120
+ output = output + "Total Repositories: " + str(stats.total) + "\n"
121
+ output = output + "Public: " + str(stats.public) + " (" + calc_percent(stats.public, stats.total) + "%)\n"
122
+ output = output + "Private: " + str(stats.private) + " (" + calc_percent(stats.private, stats.total) + "%)\n"
123
+ output = output + "Recently Updated: " + str(stats.recently_updated_7days) + " (last 7 days)\n"
124
+ output = output + "\n"
125
+
126
+ // 최근 업데이트 저장소
127
+ if length(stats.by_updated) > 0 {
128
+ output = output + "⏰ Recently Updated (Top 10)\n"
129
+ output = output + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
130
+
131
+ var i: i32 = 0
132
+ while i < length(stats.by_updated) && i < 10 {
133
+ var repo = stats.by_updated[i]
134
+ output = output + str(i + 1) + ". " + repo.full_name + "\n"
135
+ output = output + " Updated: " + repo.updated_at + "\n"
136
+ i = i + 1
137
+ }
138
+
139
+ output = output + "\n"
140
+ }
141
+
142
+ output = output + "Analysis completed at: " + get_timestamp() + "\n"
143
+
144
+ CliResult {
145
+ exit_code: 0,
146
+ output: output,
147
+ error: ""
148
+ }
149
+ }
150
+
151
+ /// JSON 포맷 분석 결과
152
+ fn analyze_json(stats: RepositoryStats) -> CliResult {
153
+ var json = "{\n"
154
+ json = json + " \"total\": " + str(stats.total) + ",\n"
155
+ json = json + " \"public\": " + str(stats.public) + ",\n"
156
+ json = json + " \"private\": " + str(stats.private) + ",\n"
157
+ json = json + " \"public_percent\": " + str(calc_percent(stats.public, stats.total)) + ",\n"
158
+ json = json + " \"private_percent\": " + str(calc_percent(stats.private, stats.total)) + ",\n"
159
+ json = json + " \"recently_updated_7days\": " + str(stats.recently_updated_7days) + ",\n"
160
+ json = json + " \"recently_updated_repos\": [\n"
161
+
162
+ var i: i32 = 0
163
+ while i < length(stats.by_updated) && i < 10 {
164
+ var repo = stats.by_updated[i]
165
+ json = json + " {\"name\": \"" + repo.full_name + "\", \"updated_at\": \"" + repo.updated_at + "\"}"
166
+ if i < length(stats.by_updated) - 1 { json = json + "," }
167
+ json = json + "\n"
168
+ i = i + 1
169
+ }
170
+
171
+ json = json + " ]\n"
172
+ json = json + "}\n"
173
+
174
+ CliResult {
175
+ exit_code: 0,
176
+ output: json,
177
+ error: ""
178
+ }
179
+ }
180
+
181
+ /// YAML 포맷 분석 결과
182
+ fn analyze_yaml(stats: RepositoryStats) -> CliResult {
183
+ var yaml = "repository_analysis:\n"
184
+ yaml = yaml + " total: " + str(stats.total) + "\n"
185
+ yaml = yaml + " public: " + str(stats.public) + "\n"
186
+ yaml = yaml + " private: " + str(stats.private) + "\n"
187
+ yaml = yaml + " public_percent: " + str(calc_percent(stats.public, stats.total)) + "%\n"
188
+ yaml = yaml + " private_percent: " + str(calc_percent(stats.private, stats.total)) + "%\n"
189
+ yaml = yaml + " recently_updated_7days: " + str(stats.recently_updated_7days) + "\n"
190
+ yaml = yaml + " recently_updated_repos:\n"
191
+
192
+ var i: i32 = 0
193
+ while i < length(stats.by_updated) && i < 10 {
194
+ var repo = stats.by_updated[i]
195
+ yaml = yaml + " - name: " + repo.full_name + "\n"
196
+ yaml = yaml + " updated_at: " + repo.updated_at + "\n"
197
+ i = i + 1
198
+ }
199
+
200
+ CliResult {
201
+ exit_code: 0,
202
+ output: yaml,
203
+ error: ""
204
+ }
205
+ }
206
+
207
+ // ==================== 유틸리티 ====================
208
+
209
+ /// 퍼센트 계산
210
+ fn calc_percent(part: i32, total: i32) -> i32 {
211
+ if total == 0 {
212
+ return 0
213
+ }
214
+ (part * 100) / total
215
+ }
216
+
217
+ /// 현재 타임스탬프
218
+ fn get_timestamp() -> str {
219
+ // TODO: 현재 시간 구현
220
+ "" // placeholder
221
+ }
222
+
223
+ // 필요한 함수들:
224
+ // - get_format
225
+ // - get_default_host, new_client, new_cache
226
+ // - new_repo_service, list_repos
227
+ // - error_message
@@ -0,0 +1,315 @@
1
+ // FreeLang v4.2 — gogs-cli Phase 5 인증 명령어
2
+ // gogs auth login, gogs auth status
3
+ // 150라인
4
+
5
+ /// 로그인 명령어
6
+ /// gogs auth login --host URL --token TOKEN [--name hostname]
7
+ async fn cmd_auth_login(args: CliArgs) -> CliResult {
8
+ // 1. 플래그 파싱
9
+ var host_url = get_flag(args, "host")
10
+ var token = get_flag(args, "token")
11
+ var host_name = get_flag(args, "name")
12
+
13
+ // 2. 입력 검증
14
+ if host_url == "" {
15
+ return CliResult {
16
+ exit_code: 1,
17
+ output: "",
18
+ error: "--host URL is required"
19
+ }
20
+ }
21
+
22
+ if token == "" {
23
+ return CliResult {
24
+ exit_code: 1,
25
+ output: "",
26
+ error: "--token TOKEN is required"
27
+ }
28
+ }
29
+
30
+ // 3. 호스트 이름 기본값 설정 (URL에서 추출)
31
+ if host_name == "" {
32
+ host_name = extract_host_name(host_url)
33
+ }
34
+
35
+ // 4. 연결 테스트
36
+ println("Testing connection to " + host_url + "...")
37
+
38
+ var client = new_client(host_url, token)
39
+ var test_resp = await http_get(client, "/api/v1/user")
40
+
41
+ if is_error(test_resp.status) {
42
+ var error_msg = parse_http_error(test_resp)
43
+ return CliResult {
44
+ exit_code: 1,
45
+ output: "",
46
+ error: "Connection failed: " + error_msg
47
+ }
48
+ }
49
+
50
+ // 5. 설정 저장
51
+ var cfg = await add_host(host_name, host_url, token)
52
+
53
+ // 6. 성공 메시지
54
+ var output = ""
55
+ output = output + "✓ Logged in successfully\n"
56
+ output = output + " Host: " + host_name + "\n"
57
+ output = output + " URL: " + host_url + "\n"
58
+
59
+ CliResult {
60
+ exit_code: 0,
61
+ output: output,
62
+ error: ""
63
+ }
64
+ }
65
+
66
+ /// 상태 확인 명령어
67
+ /// gogs auth status
68
+ async fn cmd_auth_status(args: CliArgs) -> CliResult {
69
+ // 1. 기본 호스트 확인
70
+ var host = await get_default_host()
71
+
72
+ if host.url == "" {
73
+ return CliResult {
74
+ exit_code: 1,
75
+ output: "",
76
+ error: "Not logged in. Run: gogs auth login --host URL --token TOKEN"
77
+ }
78
+ }
79
+
80
+ // 2. 사용자 정보 조회
81
+ var client = new_client(host.url, host.token)
82
+ var resp = await http_get(client, "/api/v1/user")
83
+
84
+ if is_error(resp.status) {
85
+ var error_msg = parse_http_error(resp)
86
+ return CliResult {
87
+ exit_code: 1,
88
+ output: "",
89
+ error: "Failed to fetch user info: " + error_msg
90
+ }
91
+ }
92
+
93
+ // 3. 사용자 정보 파싱
94
+ var user_data = json_parse(resp.body) as User
95
+
96
+ // 4. 모든 호스트 목록 조회
97
+ var all_hosts = await list_hosts()
98
+
99
+ // 5. 포맷 선택
100
+ var format = get_format(args)
101
+
102
+ if format == "json" {
103
+ return auth_status_json(user_data, host, all_hosts)
104
+ }
105
+
106
+ if format == "yaml" {
107
+ return auth_status_yaml(user_data, host, all_hosts)
108
+ }
109
+
110
+ // 기본: 텍스트
111
+ auth_status_text(user_data, host, all_hosts)
112
+ }
113
+
114
+ // ==================== 헬퍼 함수 ====================
115
+
116
+ /// URL에서 호스트 이름 추출
117
+ fn extract_host_name(url: str) -> str {
118
+ // "http://localhost:15000" -> "localhost"
119
+ // "https://gogs.example.com" -> "example.com"
120
+
121
+ // 프로토콜 제거
122
+ var result = url
123
+ if startsWith(result, "https://") {
124
+ result = substring(result, 8, length(result))
125
+ } else if startsWith(result, "http://") {
126
+ result = substring(result, 7, length(result))
127
+ }
128
+
129
+ // 포트 제거
130
+ var colon_idx = indexOf(result, ":")
131
+ if colon_idx != -1 {
132
+ result = substring(result, 0, colon_idx)
133
+ }
134
+
135
+ // 슬래시 제거
136
+ var slash_idx = indexOf(result, "/")
137
+ if slash_idx != -1 {
138
+ result = substring(result, 0, slash_idx)
139
+ }
140
+
141
+ result
142
+ }
143
+
144
+ /// HTTP 에러 메시지 파싱
145
+ fn parse_http_error(resp: HttpResponse) -> str {
146
+ if resp.status == 401 {
147
+ return "Unauthorized (invalid token)"
148
+ }
149
+ if resp.status == 403 {
150
+ return "Forbidden"
151
+ }
152
+ if resp.status == 404 {
153
+ return "Not found"
154
+ }
155
+ if resp.status >= 500 {
156
+ return "Server error (HTTP " + str(resp.status) + ")"
157
+ }
158
+ if resp.status >= 400 {
159
+ return "Bad request (HTTP " + str(resp.status) + ")"
160
+ }
161
+
162
+ "Connection error"
163
+ }
164
+
165
+ // ==================== 출력 포맷 ====================
166
+
167
+ /// 텍스트 포맷 상태 출력
168
+ fn auth_status_text(user: User, host: HostConfig, all_hosts: [HostConfig]) -> CliResult {
169
+ var output = ""
170
+ output = output + "╔════════════════════════════════════════╗\n"
171
+ output = output + "║ Authentication Status ║\n"
172
+ output = output + "╚════════════════════════════════════════╝\n"
173
+ output = output + "\n"
174
+ output = output + "Current Host:\n"
175
+ output = output + " Name: " + host.name + "\n"
176
+ output = output + " URL: " + host.url + "\n"
177
+ output = output + "\n"
178
+ output = output + "User:\n"
179
+ output = output + " Login: " + user.login + "\n"
180
+ output = output + " Full Name: " + user.full_name + "\n"
181
+ output = output + " Email: " + user.email + "\n"
182
+ output = output + " Admin: " + bool_to_str(user.is_admin) + "\n"
183
+ output = output + "\n"
184
+ output = output + "Configured Hosts: (" + str(length(all_hosts)) + ")\n"
185
+
186
+ var i: i32 = 0
187
+ while i < length(all_hosts) {
188
+ var h = all_hosts[i]
189
+ var mark = if h.name == host.name then "✓" else " "
190
+ output = output + " [" + mark + "] " + h.name + " (" + h.url + ")\n"
191
+ i = i + 1
192
+ }
193
+
194
+ CliResult {
195
+ exit_code: 0,
196
+ output: output,
197
+ error: ""
198
+ }
199
+ }
200
+
201
+ /// JSON 포맷 상태 출력
202
+ fn auth_status_json(user: User, host: HostConfig, all_hosts: [HostConfig]) -> CliResult {
203
+ var json_str = "{\n"
204
+ json_str = json_str + " \"status\": \"authenticated\",\n"
205
+ json_str = json_str + " \"current_host\": {\n"
206
+ json_str = json_str + " \"name\": \"" + host.name + "\",\n"
207
+ json_str = json_str + " \"url\": \"" + host.url + "\"\n"
208
+ json_str = json_str + " },\n"
209
+ json_str = json_str + " \"user\": {\n"
210
+ json_str = json_str + " \"login\": \"" + user.login + "\",\n"
211
+ json_str = json_str + " \"full_name\": \"" + user.full_name + "\",\n"
212
+ json_str = json_str + " \"email\": \"" + user.email + "\",\n"
213
+ json_str = json_str + " \"is_admin\": " + bool_to_json(user.is_admin) + "\n"
214
+ json_str = json_str + " },\n"
215
+ json_str = json_str + " \"hosts\": [\n"
216
+
217
+ var i: i32 = 0
218
+ while i < length(all_hosts) {
219
+ var h = all_hosts[i]
220
+ json_str = json_str + " {\n"
221
+ json_str = json_str + " \"name\": \"" + h.name + "\",\n"
222
+ json_str = json_str + " \"url\": \"" + h.url + "\",\n"
223
+ json_str = json_str + " \"current\": " + bool_to_json(h.name == host.name) + "\n"
224
+ json_str = json_str + " }"
225
+ if i < length(all_hosts) - 1 {
226
+ json_str = json_str + ","
227
+ }
228
+ json_str = json_str + "\n"
229
+ i = i + 1
230
+ }
231
+
232
+ json_str = json_str + " ]\n"
233
+ json_str = json_str + "}\n"
234
+
235
+ CliResult {
236
+ exit_code: 0,
237
+ output: json_str,
238
+ error: ""
239
+ }
240
+ }
241
+
242
+ /// YAML 포맷 상태 출력
243
+ fn auth_status_yaml(user: User, host: HostConfig, all_hosts: [HostConfig]) -> CliResult {
244
+ var yaml_str = "status: authenticated\n"
245
+ yaml_str = yaml_str + "current_host:\n"
246
+ yaml_str = yaml_str + " name: " + host.name + "\n"
247
+ yaml_str = yaml_str + " url: " + host.url + "\n"
248
+ yaml_str = yaml_str + "user:\n"
249
+ yaml_str = yaml_str + " login: " + user.login + "\n"
250
+ yaml_str = yaml_str + " full_name: " + user.full_name + "\n"
251
+ yaml_str = yaml_str + " email: " + user.email + "\n"
252
+ yaml_str = yaml_str + " is_admin: " + bool_to_str(user.is_admin) + "\n"
253
+ yaml_str = yaml_str + "hosts:\n"
254
+
255
+ var i: i32 = 0
256
+ while i < length(all_hosts) {
257
+ var h = all_hosts[i]
258
+ yaml_str = yaml_str + " - name: " + h.name + "\n"
259
+ yaml_str = yaml_str + " url: " + h.url + "\n"
260
+ yaml_str = yaml_str + " current: " + bool_to_str(h.name == host.name) + "\n"
261
+ i = i + 1
262
+ }
263
+
264
+ CliResult {
265
+ exit_code: 0,
266
+ output: yaml_str,
267
+ error: ""
268
+ }
269
+ }
270
+
271
+ // ==================== 유틸리티 ====================
272
+
273
+ /// bool을 문자열로 변환
274
+ fn bool_to_str(b: bool) -> str {
275
+ if b {
276
+ return "true"
277
+ }
278
+ "false"
279
+ }
280
+
281
+ /// bool을 JSON 값으로 변환
282
+ fn bool_to_json(b: bool) -> str {
283
+ if b {
284
+ return "true"
285
+ }
286
+ "false"
287
+ }
288
+
289
+ /// 문자열이 특정 문자를 포함하는지 확인
290
+ fn indexOf(s: str, substr: str) -> i32 {
291
+ var i: i32 = 0
292
+ var len = length(s)
293
+ var sublen = length(substr)
294
+
295
+ while i <= len - sublen {
296
+ if substring(s, i, i + sublen) == substr {
297
+ return i
298
+ }
299
+ i = i + 1
300
+ }
301
+
302
+ -1 // not found
303
+ }
304
+
305
+ /// 필요한 함수들 (FreeLang 내장 또는 다른 모듈에서)
306
+ // - startsWith(s: str, prefix: str) -> bool
307
+ // - substring(s: str, start: i32, end: i32) -> str
308
+ // - length(s: str) -> i32
309
+ // - indexOf(s: str, substr: str) -> i32
310
+ // - json_parse(json: str) -> T
311
+ // - http_get(client: HttpClient, path: str) -> HttpResponse
312
+ // - is_error(status: i32) -> bool
313
+ // - add_host(name: str, url: str, token: str) -> Config
314
+ // - get_default_host() -> HostConfig
315
+ // - list_hosts() -> [HostConfig]