omneval-devloop 0.0.2__tar.gz → 0.0.3__tar.gz

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 (86) hide show
  1. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/release.yml +9 -2
  2. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/PKG-INFO +1 -1
  3. omneval_devloop-0.0.3/charts/devloop/Chart.yaml +10 -0
  4. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/templates/discord-bot-deployment.yaml +24 -0
  5. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/templates/poller-deployment.yaml +46 -0
  6. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/templates/temporal-worker-deployment.yaml +10 -0
  7. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/values.yaml +47 -2
  8. omneval_devloop-0.0.2/charts/devloop/Chart.yaml +0 -6
  9. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/build-agent-base-image.yml +0 -0
  10. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/build-image.yml +0 -0
  11. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/ci.yml +0 -0
  12. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/publish-devloop-chart.yml +0 -0
  13. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/publish-discord-bot-image.yml +0 -0
  14. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/publish-poller-image.yml +0 -0
  15. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.github/workflows/publish-temporal-worker-image.yml +0 -0
  16. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/.gitignore +0 -0
  17. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/CODEOWNERS +0 -0
  18. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/CONTEXT.md +0 -0
  19. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/LICENSE +0 -0
  20. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/README.md +0 -0
  21. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/templates/NOTES.txt +0 -0
  22. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/templates/_helpers.tpl +0 -0
  23. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/templates/temporal-worker-service.yaml +0 -0
  24. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/charts/devloop/test-values.yaml +0 -0
  25. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/docs/getting-started.md +0 -0
  26. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/docs/temporal-prerequisites.md +0 -0
  27. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/Dockerfile +0 -0
  28. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/entrypoint.py +0 -0
  29. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/prompts/diagnosis.md +0 -0
  30. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/prompts/implement.md +0 -0
  31. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/prompts/merge.md +0 -0
  32. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/prompts/plan.md +0 -0
  33. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/prompts/review.md +0 -0
  34. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/pyproject.toml +0 -0
  35. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/pytest.ini +0 -0
  36. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/test_entrypoint.py +0 -0
  37. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/test_human_question.py +0 -0
  38. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/test_project_tests.py +0 -0
  39. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/agent-base/test_run_agent.py +0 -0
  40. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/Dockerfile +0 -0
  41. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/activities.py +0 -0
  42. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/discord_client.py +0 -0
  43. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/main.py +0 -0
  44. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/pyproject.toml +0 -0
  45. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/pytest.ini +0 -0
  46. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/test_text_utils.py +0 -0
  47. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/test_thread_store.py +0 -0
  48. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/text_utils.py +0 -0
  49. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/thread_store.py +0 -0
  50. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/discord-bot/uv.lock +0 -0
  51. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/poller/Dockerfile +0 -0
  52. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/poller/poll.py +0 -0
  53. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/poller/pyproject.toml +0 -0
  54. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/poller/pytest.ini +0 -0
  55. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/poller/test_poll.py +0 -0
  56. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/poller/uv.lock +0 -0
  57. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/temporal-worker/Dockerfile +0 -0
  58. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/images/temporal-worker/pyproject.toml +0 -0
  59. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/plan.md +0 -0
  60. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/pyproject.toml +0 -0
  61. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/__init__.py +0 -0
  62. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/cluster.py +0 -0
  63. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/dev_loop.py +0 -0
  64. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/dev_loop_logic.py +0 -0
  65. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/github_ops.py +0 -0
  66. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/k8s_jobs.py +0 -0
  67. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/projects.py +0 -0
  68. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/schedules.py +0 -0
  69. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/shared.py +0 -0
  70. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/summarization.py +0 -0
  71. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/summarize_activities.py +0 -0
  72. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/webhook.py +0 -0
  73. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/worker.py +0 -0
  74. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/src/devloop/workflows.py +0 -0
  75. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_cluster.py +0 -0
  76. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_dev_loop.py +0 -0
  77. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_docs.py +0 -0
  78. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_github_ops.py +0 -0
  79. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_github_webhook.py +0 -0
  80. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_k8s_jobs.py +0 -0
  81. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_projects.py +0 -0
  82. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_pure_logic.py +0 -0
  83. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_stub_roundtrip.py +0 -0
  84. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_summarization.py +0 -0
  85. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/tests/test_worker.py +0 -0
  86. {omneval_devloop-0.0.2 → omneval_devloop-0.0.3}/uv.lock +0 -0
@@ -87,7 +87,7 @@ jobs:
87
87
 
88
88
  chart:
89
89
  name: Package and push Helm chart
90
- needs: test
90
+ needs: [test, meta]
91
91
  runs-on: ubuntu-latest
92
92
  permissions:
93
93
  packages: write
@@ -109,8 +109,15 @@ jobs:
109
109
 
110
110
  - name: Package chart
111
111
  id: package
112
+ # The git tag is the single source of truth (see header): override the
113
+ # placeholder version in Chart.yaml so the chart publishes at the same
114
+ # semver as the SDK and images. Without --version, helm reads Chart.yaml
115
+ # and a v0.0.2 tag would publish e.g. chart 0.1.0 — out of step with the
116
+ # 0.0.2 images.
112
117
  run: |
113
- helm package charts/devloop/ -u -d .cr-release-packages
118
+ helm package charts/devloop/ -u -d .cr-release-packages \
119
+ --version "${{ needs.meta.outputs.version }}" \
120
+ --app-version "${{ needs.meta.outputs.version }}"
114
121
  echo "chart=$(ls .cr-release-packages/devloop-*.tgz)" >> "$GITHUB_OUTPUT"
115
122
 
116
123
  - name: Lint chart
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omneval-devloop
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  License-File: LICENSE
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: fastapi>=0.115.6
@@ -0,0 +1,10 @@
1
+ apiVersion: v2
2
+ name: devloop
3
+ description: Helm chart for deploying the devloop components (Temporal Orchestration Worker, Discord Bot, GitHub Issue Poller)
4
+ type: application
5
+ # Placeholder only. The release pipeline (release.yml) packages the chart with
6
+ # --version/--app-version set to the git tag, so the PUBLISHED chart semver always
7
+ # matches the SDK and image releases. This static value is used solely by PR lint
8
+ # and local `helm template`; it is never what gets pushed to the OCI registry.
9
+ version: 0.0.0
10
+ appVersion: "0.0.0"
@@ -19,6 +19,13 @@ spec:
19
19
  app.kubernetes.io/component: discord-bot
20
20
  app.kubernetes.io/instance: {{ .Release.Name }}
21
21
  spec:
22
+ {{- if .Values.discordBot.serviceAccountName }}
23
+ serviceAccountName: {{ .Values.discordBot.serviceAccountName }}
24
+ {{- end }}
25
+ {{- with .Values.discordBot.imagePullSecrets }}
26
+ imagePullSecrets:
27
+ {{- toYaml . | nindent 8 }}
28
+ {{- end }}
22
29
  containers:
23
30
  - name: discord-bot
24
31
  image: "{{ .Values.discordBot.image.repository }}:{{ .Values.discordBot.image.tag }}"
@@ -31,11 +38,28 @@ spec:
31
38
  - name: TEMPORAL_HOST
32
39
  value: {{ .Values.temporalHost | quote }}
33
40
  - name: DISCORD_TOKEN
41
+ {{- if .Values.discordBot.tokenSecret }}
42
+ valueFrom:
43
+ secretKeyRef:
44
+ name: {{ .Values.discordBot.tokenSecret.name }}
45
+ key: {{ .Values.discordBot.tokenSecret.key }}
46
+ {{- else }}
34
47
  value: {{ .Values.discordBot.token | quote }}
48
+ {{- end }}
49
+ {{- with .Values.discordBot.applicationIdSecret }}
50
+ - name: DISCORD_APPLICATION_ID
51
+ valueFrom:
52
+ secretKeyRef:
53
+ name: {{ .name }}
54
+ key: {{ .key }}
55
+ {{- end }}
35
56
  - name: TASK_QUEUE
36
57
  value: {{ .Values.discordBot.taskQueue | quote }}
37
58
  - name: HEALTH_PORT
38
59
  value: {{ .Values.discordBot.healthPort | quote }}
60
+ {{- with .Values.discordBot.extraEnv }}
61
+ {{- toYaml . | nindent 12 }}
62
+ {{- end }}
39
63
  {{- include "devloop.healthProbes" . | nindent 10 }}
40
64
  {{- if .Values.discordBot.resources }}
41
65
  resources:
@@ -2,6 +2,26 @@
2
2
  {{- if and .Values.poller.enabled .Values.poller.projects }}
3
3
  {{- range $idx, $project := .Values.poller.projects }}
4
4
  {{- $sanitizedRepoName := regexReplaceAll "[^a-zA-Z0-9]" .repo "" | lower | trunc 52 -}}
5
+ {{- if $.Values.poller.persistence.enabled }}
6
+ ---
7
+ apiVersion: v1
8
+ kind: PersistentVolumeClaim
9
+ metadata:
10
+ name: {{ $.Release.Name }}-poller-{{ $sanitizedRepoName }}-state
11
+ labels:
12
+ {{- include "devloop.labels" $ | nindent 4 }}
13
+ app.kubernetes.io/component: poller
14
+ app.kubernetes.io/project: {{ .repo | quote }}
15
+ spec:
16
+ accessModes:
17
+ - {{ $.Values.poller.persistence.accessMode }}
18
+ {{- if $.Values.poller.persistence.storageClassName }}
19
+ storageClassName: {{ $.Values.poller.persistence.storageClassName }}
20
+ {{- end }}
21
+ resources:
22
+ requests:
23
+ storage: {{ $.Values.poller.persistence.size }}
24
+ {{- end }}
5
25
  ---
6
26
  apiVersion: apps/v1
7
27
  kind: Deployment
@@ -25,6 +45,10 @@ spec:
25
45
  app.kubernetes.io/instance: {{ $.Release.Name }}
26
46
  app.kubernetes.io/project: {{ .repo | quote }}
27
47
  spec:
48
+ {{- with $.Values.poller.imagePullSecrets }}
49
+ imagePullSecrets:
50
+ {{- toYaml . | nindent 8 }}
51
+ {{- end }}
28
52
  containers:
29
53
  - name: poller
30
54
  image: "{{ $.Values.poller.image.repository }}:{{ $.Values.poller.image.tag }}"
@@ -35,7 +59,14 @@ spec:
35
59
  protocol: TCP
36
60
  env:
37
61
  - name: GITHUB_TOKEN
62
+ {{- if $.Values.poller.githubTokenSecret }}
63
+ valueFrom:
64
+ secretKeyRef:
65
+ name: {{ $.Values.poller.githubTokenSecret.name }}
66
+ key: {{ $.Values.poller.githubTokenSecret.key }}
67
+ {{- else }}
38
68
  value: {{ $.Values.poller.githubToken | quote }}
69
+ {{- end }}
39
70
  - name: GITHUB_REPO
40
71
  value: {{ .repo | quote }}
41
72
  - name: AGENT_LABEL
@@ -46,10 +77,25 @@ spec:
46
77
  value: {{ $.Values.poller.pollIntervalSeconds | quote }}
47
78
  - name: HEALTH_PORT
48
79
  value: {{ $.Values.poller.healthPort | quote }}
80
+ {{- if $.Values.poller.persistence.enabled }}
81
+ - name: STATE_FILE
82
+ value: {{ printf "%s/state.json" $.Values.poller.persistence.mountPath | quote }}
83
+ {{- end }}
84
+ {{- if $.Values.poller.persistence.enabled }}
85
+ volumeMounts:
86
+ - name: state
87
+ mountPath: {{ $.Values.poller.persistence.mountPath }}
88
+ {{- end }}
49
89
  {{- include "devloop.healthProbes" $ | nindent 10 }}
50
90
  {{- if $.Values.poller.resources }}
51
91
  resources:
52
92
  {{- toYaml $.Values.poller.resources | nindent 12 }}
53
93
  {{- end }}
94
+ {{- if $.Values.poller.persistence.enabled }}
95
+ volumes:
96
+ - name: state
97
+ persistentVolumeClaim:
98
+ claimName: {{ $.Release.Name }}-poller-{{ $sanitizedRepoName }}-state
99
+ {{- end }}
54
100
  {{- end }}
55
101
  {{- end }}
@@ -19,6 +19,13 @@ spec:
19
19
  app.kubernetes.io/component: temporal-worker
20
20
  app.kubernetes.io/instance: {{ .Release.Name }}
21
21
  spec:
22
+ {{- if .Values.temporalWorker.serviceAccountName }}
23
+ serviceAccountName: {{ .Values.temporalWorker.serviceAccountName }}
24
+ {{- end }}
25
+ {{- with .Values.temporalWorker.imagePullSecrets }}
26
+ imagePullSecrets:
27
+ {{- toYaml . | nindent 8 }}
28
+ {{- end }}
22
29
  containers:
23
30
  - name: temporal-worker
24
31
  image: "{{ .Values.temporalWorker.image.repository }}:{{ .Values.temporalWorker.image.tag }}"
@@ -41,6 +48,9 @@ spec:
41
48
  value: {{ .Values.temporalWorker.webhookPort | quote }}
42
49
  - name: PROJECTS_FILE
43
50
  value: {{ .Values.temporalWorker.projectsFile | quote }}
51
+ {{- with .Values.temporalWorker.extraEnv }}
52
+ {{- toYaml . | nindent 12 }}
53
+ {{- end }}
44
54
  {{- include "devloop.healthProbes" . | nindent 10 }}
45
55
  {{- if .Values.temporalWorker.resources }}
46
56
  resources:
@@ -12,6 +12,20 @@ temporalWorker:
12
12
  repository: ghcr.io/omneval/devloop-temporal-worker
13
13
  tag: "latest"
14
14
  pullPolicy: IfNotPresent
15
+ # Name of a pre-existing ServiceAccount to run the pod as (e.g. one bound to a
16
+ # Role that can create Agent Execution Jobs). Empty uses the namespace default.
17
+ serviceAccountName: ""
18
+ # Image pull secrets (e.g. when the worker image lives in a private registry).
19
+ imagePullSecrets: []
20
+ # - name: regcred
21
+ # Extra environment variables appended verbatim to the container. Use this for
22
+ # consumer-specific config (alternate task queues, custom workflow settings,
23
+ # agent LLM endpoints, etc.). Entries are standard core/v1 EnvVar objects.
24
+ extraEnv: []
25
+ # - name: ORCHESTRATION_QUEUE
26
+ # value: "homelab-orchestration"
27
+ # - name: AGENT_MODEL
28
+ # value: "openai/my-model"
15
29
  # Resource limits for the worker container
16
30
  resources: {}
17
31
  # requests:
@@ -40,8 +54,24 @@ discordBot:
40
54
  repository: ghcr.io/omneval/devloop-discord-bot
41
55
  tag: "latest"
42
56
  pullPolicy: IfNotPresent
43
- # Discord bot token (use a Kubernetes Secret in production)
57
+ # Discord bot token. Prefer `tokenSecret` (a Kubernetes Secret reference) over
58
+ # the plain `token` value, which is only suitable for local/dev use.
44
59
  token: ""
60
+ # tokenSecret: { name: "discord-bot", key: "TOKEN" }
61
+ tokenSecret: {}
62
+ # Optional Discord application ID, sourced from a Secret the same way.
63
+ # applicationIdSecret: { name: "discord-bot", key: "APPLICATION_ID" }
64
+ applicationIdSecret: {}
65
+ # Name of a pre-existing ServiceAccount (e.g. one allowed to read/write the
66
+ # thread-map ConfigMap). Empty uses the namespace default.
67
+ serviceAccountName: ""
68
+ # Image pull secrets for a private bot image.
69
+ imagePullSecrets: []
70
+ # Extra environment variables appended verbatim (e.g. channel IDs,
71
+ # K8S_NAMESPACE, CONFIGMAP_NAME). Standard core/v1 EnvVar objects.
72
+ extraEnv: []
73
+ # - name: DISCORD_CHANNEL_APPROVALS
74
+ # value: "123456789"
45
75
  # Resource limits
46
76
  resources: {}
47
77
  # requests:
@@ -64,12 +94,27 @@ discordBot:
64
94
  # GITHUB_TOKEN is shared across all pollers; repo-specific fields are per-entry.
65
95
  poller:
66
96
  enabled: true
67
- # Shared GitHub token for all poller instances (use a Kubernetes Secret)
97
+ # Shared GitHub token for all poller instances. Prefer `githubTokenSecret`
98
+ # (a Kubernetes Secret reference) over the plain `githubToken` value.
68
99
  githubToken: ""
100
+ # githubTokenSecret: { name: "agent-github-token", key: "GITHUB_TOKEN" }
101
+ githubTokenSecret: {}
69
102
  image:
70
103
  repository: ghcr.io/omneval/devloop-poller
71
104
  tag: "latest"
72
105
  pullPolicy: IfNotPresent
106
+ # Image pull secrets for a private poller image.
107
+ imagePullSecrets: []
108
+ # Persist poll state across restarts. Without this the poller re-notifies
109
+ # every issue on each restart (the state file lives on an emptyDir otherwise).
110
+ # When enabled, a PVC per poller deployment is created and mounted at
111
+ # `mountPath`, and STATE_FILE points inside it.
112
+ persistence:
113
+ enabled: false
114
+ storageClassName: ""
115
+ accessMode: ReadWriteOnce
116
+ size: 100Mi
117
+ mountPath: /data
73
118
  # Resource limits (applied to all poller instances)
74
119
  resources: {}
75
120
  # requests:
@@ -1,6 +0,0 @@
1
- apiVersion: v2
2
- name: devloop
3
- description: Helm chart for deploying the devloop components (Temporal Orchestration Worker, Discord Bot, GitHub Issue Poller)
4
- type: application
5
- version: 0.1.0
6
- appVersion: "0.1.0"
File without changes
File without changes
File without changes