agent-canvas 0.2.0 → 0.6.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.
- package/README.md +42 -17
- package/bin/agent-canvas.js +7 -7
- package/bin/agent-canvas.ts +48 -37
- package/bin/lib/commands/daemon.ts +46 -43
- package/bin/lib/commands/exec.ts +100 -100
- package/bin/lib/commands/fetch.ts +15 -15
- package/bin/lib/commands/install.ts +36 -36
- package/bin/lib/commands/instructions.ts +51 -51
- package/bin/lib/commands/push.ts +115 -97
- package/bin/lib/commands/review.ts +190 -0
- package/bin/lib/commands/watch.ts +24 -24
- package/bin/lib/config.ts +12 -12
- package/bin/lib/daemon-lifecycle.ts +47 -51
- package/bin/lib/helpers.ts +82 -76
- package/bin/lib/instructions/app-review.md +88 -0
- package/bin/lib/instructions/component-checklist.md +7 -5
- package/bin/lib/instructions/component-custom.md +20 -14
- package/bin/lib/instructions/component-diff.md +4 -3
- package/bin/lib/instructions/component-item.md +18 -8
- package/bin/lib/instructions/component-markdown.md +4 -2
- package/bin/lib/instructions/component-mermaid.md +12 -6
- package/bin/lib/instructions/component-multichoice.md +6 -2
- package/bin/lib/instructions/component-note.md +1 -1
- package/bin/lib/instructions/component-priority.md +1 -1
- package/bin/lib/instructions/component-rangeinput.md +9 -2
- package/bin/lib/instructions/component-secretinput.md +12 -11
- package/bin/lib/instructions/component-svg.md +62 -16
- package/bin/lib/instructions/component-table.md +6 -6
- package/bin/lib/instructions/component-usefeedback.md +8 -8
- package/bin/lib/instructions/component-userinput.md +6 -2
- package/bin/lib/instructions/flow-feature.md +22 -5
- package/bin/lib/instructions/flow-plan.md +5 -5
- package/bin/lib/instructions/flow-runbook.md +28 -25
- package/bin/lib/instructions/overview.md +27 -18
- package/bin/lib/instructions/theming.md +5 -0
- package/daemon/build.ts +171 -152
- package/daemon/client/App.tsx +1205 -1077
- package/daemon/client/CompareView.tsx +478 -458
- package/daemon/client/FileBrowser.tsx +241 -203
- package/daemon/client/FileViewer.tsx +220 -211
- package/daemon/client/OverviewView.tsx +283 -267
- package/daemon/client/ReviewerIdentityDialog.tsx +72 -71
- package/daemon/client/RevisionSelect.tsx +111 -95
- package/daemon/client/SessionSwitcher.tsx +81 -81
- package/daemon/client/ShareDialog.tsx +211 -212
- package/daemon/client/apiSchemas.ts +78 -0
- package/daemon/client/canvas-runtime.ts +1 -1
- package/daemon/client/clientApi.ts +194 -206
- package/daemon/client/components/SecretInput.tsx +138 -138
- package/daemon/client/components/index.ts +2 -2
- package/daemon/client/domDiff.ts +341 -333
- package/daemon/client/errorMessage.ts +3 -0
- package/daemon/client/exportMarkdown.ts +222 -201
- package/daemon/client/lcs.ts +19 -14
- package/daemon/client/shareCrypto.ts +57 -57
- package/daemon/client/unifiedDiff.ts +660 -649
- package/daemon/dist/annotations-runtime.js +1 -1
- package/daemon/dist/client.css +2 -2
- package/daemon/dist/client.js +77 -80
- package/daemon/dist/components.js +59 -59
- package/daemon/dist/preact-compat.js +1 -1
- package/daemon/dist/review.js +361 -0
- package/daemon/dist/runtime.js +1 -1
- package/daemon/langMap.ts +24 -6
- package/daemon/review-widget/anchor.ts +187 -0
- package/daemon/review-widget/client.ts +95 -0
- package/daemon/review-widget/index.tsx +36 -0
- package/daemon/review-widget/markup.ts +71 -0
- package/daemon/review-widget/screenshot.ts +91 -0
- package/daemon/review-widget/source.ts +103 -0
- package/daemon/review-widget/styles.css +359 -0
- package/daemon/review-widget/text-modules.d.ts +4 -0
- package/daemon/review-widget/tsconfig.json +16 -0
- package/daemon/review-widget/widget.tsx +436 -0
- package/daemon/src/components.ts +3 -3
- package/daemon/src/handlers/api.ts +330 -328
- package/daemon/src/handlers/files.ts +112 -105
- package/daemon/src/handlers/static.ts +47 -46
- package/daemon/src/handlers/uploads.ts +61 -56
- package/daemon/src/paths.ts +13 -10
- package/daemon/src/remote-feedback.ts +192 -192
- package/daemon/src/review/markdown.ts +52 -0
- package/daemon/src/review/model.ts +132 -0
- package/daemon/src/review/proxy.ts +177 -0
- package/daemon/src/review/server.ts +199 -0
- package/daemon/src/review/store.ts +151 -0
- package/daemon/src/server.ts +127 -86
- package/daemon/src/share.ts +163 -169
- package/daemon/src/stubFetch.ts +7 -0
- package/daemon/tsconfig.json +19 -19
- package/package.json +47 -47
- package/skills/canvas/SKILL.md +9 -1
- package/bin/lib/commands/exec.test.ts +0 -27
- package/bin/lib/helpers.test.ts +0 -79
- package/daemon/src/remote-feedback.test.ts +0 -165
- package/daemon/src/secrets-api.test.ts +0 -123
- package/daemon/src/share-e2e.test.ts +0 -277
- package/daemon/src/share.test.ts +0 -289
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useFeedback } from '#canvas/runtime'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
|
+
import { clearLocalSecret, getLocalSecretStatus, MODE, storeLocalSecret } from '../clientApi'
|
|
4
4
|
|
|
5
|
-
const FIELD_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}
|
|
6
|
-
const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]
|
|
5
|
+
const FIELD_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/
|
|
6
|
+
const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/
|
|
7
7
|
|
|
8
8
|
interface SecretInputProps {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
id: string
|
|
10
|
+
label: string
|
|
11
|
+
env: string
|
|
12
|
+
placeholder?: string
|
|
13
|
+
required?: boolean
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/** Local-only secret handoff that never enters annotation or feedback state. */
|
|
17
17
|
export function SecretInput({ id, label, env, placeholder, required }: SecretInputProps) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
const [value, setValue] = useState('')
|
|
19
|
+
const [ready, setReady] = useState(false)
|
|
20
|
+
const [busy, setBusy] = useState(!MODE.isShared)
|
|
21
|
+
const [error, setError] = useState<string | null>(null)
|
|
22
|
+
const validFieldId = FIELD_ID_RE.test(id)
|
|
23
|
+
const validEnvName = ENV_NAME_RE.test(env)
|
|
24
|
+
const locallyAvailable = !MODE.isShared
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!locallyAvailable || !validFieldId) {
|
|
28
|
+
setBusy(false)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
let active = true
|
|
33
|
+
getLocalSecretStatus(id)
|
|
34
|
+
.then((isReady) => {
|
|
35
|
+
if (active) setReady(isReady)
|
|
36
|
+
})
|
|
37
|
+
.catch((cause: unknown) => {
|
|
38
|
+
if (active) setError(cause instanceof Error ? cause.message : 'Could not read secret status')
|
|
39
|
+
})
|
|
40
|
+
.finally(() => {
|
|
41
|
+
if (active) setBusy(false)
|
|
42
|
+
})
|
|
43
|
+
return () => {
|
|
44
|
+
active = false
|
|
45
|
+
}
|
|
46
|
+
}, [id, locallyAvailable, validFieldId])
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
const feedback = ready
|
|
49
|
+
? `**${label}**\nSecret is ready. Field: \`${id}\`. Environment variable: \`${env}\`.\n`
|
|
50
|
+
: ''
|
|
51
|
+
useFeedback(`secret:${id}`, feedback, {
|
|
52
|
+
label,
|
|
53
|
+
required: required && locallyAvailable,
|
|
54
|
+
})
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
const store = async () => {
|
|
57
|
+
if (!value || !validFieldId || !validEnvName) return
|
|
58
|
+
setBusy(true)
|
|
59
|
+
setError(null)
|
|
60
|
+
try {
|
|
61
|
+
await storeLocalSecret(id, value)
|
|
62
|
+
setValue('')
|
|
63
|
+
setReady(true)
|
|
64
|
+
} catch (cause: unknown) {
|
|
65
|
+
setError(cause instanceof Error ? cause.message : 'Could not store secret')
|
|
66
|
+
} finally {
|
|
67
|
+
setBusy(false)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
const clear = async () => {
|
|
72
|
+
setBusy(true)
|
|
73
|
+
setError(null)
|
|
74
|
+
try {
|
|
75
|
+
await clearLocalSecret(id)
|
|
76
|
+
setReady(false)
|
|
77
|
+
} catch (cause: unknown) {
|
|
78
|
+
setError(cause instanceof Error ? cause.message : 'Could not clear secret')
|
|
79
|
+
} finally {
|
|
80
|
+
setBusy(false)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
if (!locallyAvailable) {
|
|
85
|
+
return (
|
|
86
|
+
<div data-md="secretinput" data-md-label={label} className="-mx-4 px-4 py-3 my-1 rounded-lg bg-accent-amber-muted">
|
|
87
|
+
<div className="text-[13px] font-body font-medium text-text-primary">{label}</div>
|
|
88
|
+
<p className="text-[12px] font-body text-text-secondary mt-1">
|
|
89
|
+
Secret input is unavailable in shared canvases. Open this runbook on the author's local canvas daemon.
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
const configurationError = !validFieldId
|
|
96
|
+
? 'Secret field IDs may contain letters, numbers, dots, underscores, and hyphens.'
|
|
97
|
+
: !validEnvName
|
|
98
|
+
? 'Environment variable names must use letters, numbers, and underscores and may not start with a number.'
|
|
99
|
+
: null
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
return (
|
|
102
|
+
<div data-md="secretinput" data-md-label={label} className="-mx-4 px-4 py-3 my-1 rounded-lg transition-colors duration-150 hover:bg-bg-input">
|
|
103
|
+
<div className="flex items-baseline justify-between gap-3 mb-2">
|
|
104
|
+
<div className="flex items-baseline gap-2">
|
|
105
|
+
<span className="text-[13px] font-body font-medium text-text-primary">{label}</span>
|
|
106
|
+
{required && <span className="text-[10px] text-accent-red font-body">*</span>}
|
|
107
|
+
</div>
|
|
108
|
+
<span className={`text-[11px] font-body ${ready ? 'text-accent-green' : 'text-text-tertiary'}`}>
|
|
109
|
+
{busy ? 'Working…' : ready ? 'Ready in memory' : 'Not stored'}
|
|
110
|
+
</span>
|
|
111
|
+
</div>
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
113
|
+
<div className="flex gap-2">
|
|
114
|
+
<input
|
|
115
|
+
type="password"
|
|
116
|
+
value={value}
|
|
117
|
+
onChange={(event) => setValue(event.target.value)}
|
|
118
|
+
onKeyDown={(event) => {
|
|
119
|
+
if (event.key === 'Enter') {
|
|
120
|
+
event.preventDefault()
|
|
121
|
+
void store()
|
|
122
|
+
}
|
|
123
|
+
}}
|
|
124
|
+
placeholder={placeholder || 'Paste secret value'}
|
|
125
|
+
autoComplete="off"
|
|
126
|
+
spellCheck={false}
|
|
127
|
+
disabled={busy || !!configurationError}
|
|
128
|
+
className="min-w-0 flex-1 bg-bg-input text-[13px] font-mono text-text-primary rounded-lg px-3 py-2 focus:outline-none border border-border-subtle focus:border-border-hover placeholder:text-text-tertiary disabled:opacity-50 transition-colors"
|
|
129
|
+
/>
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
onClick={() => void store()}
|
|
133
|
+
disabled={busy || !value || !!configurationError}
|
|
134
|
+
className="px-3 py-2 rounded-lg bg-btn-primary text-btn-primary-text text-[12px] font-body font-medium disabled:opacity-40 disabled:cursor-default"
|
|
135
|
+
>
|
|
136
|
+
{ready ? 'Replace' : 'Store'}
|
|
137
|
+
</button>
|
|
138
|
+
{ready && (
|
|
139
|
+
<button
|
|
140
|
+
type="button"
|
|
141
|
+
onClick={() => void clear()}
|
|
142
|
+
disabled={busy}
|
|
143
|
+
className="px-3 py-2 rounded-lg border border-border-subtle text-text-secondary text-[12px] font-body font-medium hover:border-border-hover disabled:opacity-40"
|
|
144
|
+
>
|
|
145
|
+
Clear
|
|
146
|
+
</button>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
147
149
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
</div>
|
|
155
|
-
);
|
|
150
|
+
<p className="text-[11px] font-body text-text-tertiary mt-1.5">
|
|
151
|
+
Stored only in daemon memory. The agent receives field {id} for {env}, not the value.
|
|
152
|
+
</p>
|
|
153
|
+
{(configurationError || error) && <p className="text-[11px] text-accent-red font-body mt-1">{configurationError || error}</p>}
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
156
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// agent-canvas's canvas component set: the kernel's components plus the ones
|
|
2
2
|
// only this host can back (SecretInput needs the daemon's secret endpoints).
|
|
3
|
-
export * from
|
|
4
|
-
export { SecretInput } from
|
|
3
|
+
export * from '@fabrika/canvas-kernel/components'
|
|
4
|
+
export { SecretInput } from './SecretInput'
|